如何返回<TEnumerable,T>:其中TEnumerable:IEnumerable <T>

Mic*_* II 6 c# extension-methods generic-collections

目标:返回时,通用枚举类型为相同类型.

注意:这在输入类型时有效,但我不明白为什么无法推断它们.

List<T> 然后回来 List<T>

IOrderedEnumerable<T> 然后回来 IOrderedEnumerable<T>

等等

当前方法(仅在输入所有类型时有效)

public static TEnumerable WithEach<TEnumerable, T>(this TEnumerable items, Action<T> action)
where TEnumerable : IEnumerable<T>
{
    foreach (var item in items) action.Invoke(item);
    return items;
}
Run Code Online (Sandbox Code Playgroud)

仅示例

var list = new List<int>(); //TODO: Mock random values
list.WithEach(x => Console.WriteLine(x)) //Here WithEach ideally returns List<int> following orignal type List<int>
    .OrderBy(x => x) 
    .WithEach(x => Console.WriteLine(x)); //Here WithEach ideally returns IOrderedEnumerable<int> following OrderBy
Run Code Online (Sandbox Code Playgroud)

让它工作

var list = new List<int>(); //TODO: Mock random values
list.WithEach<List<int>, int>(x => Console.WriteLine(x))
    .OrderBy(x => x) 
    .WithEach<IOrderedEnumerable<int>, int>(x => Console.WriteLine(x));
Run Code Online (Sandbox Code Playgroud)

我缺少的是为什么C#无法推断类型,尽管where过滤器确实使类型准确.我理解为什么要么为方法提供全部或者没有通用类型,所以请不要指出我的答案.

编辑:如果我不能推断类型; 那怎么能让这更优雅呢?

Jon*_*eet 9

C#中的类型推断是非常复杂的 - 只是一次,我不会试图通过它来解决它,因为我知道它会变得多么可怕.

认为问题是参数/参数组合都没有给编译器足够的信息来推断T:

  • TEnumerable items参数未提及T,因此T尽管存在类型约束,但它不用于推断
  • Action<T>参数将被罚款,但是编译器不能使根据您所提供的lambda表达式推断

我想不出一个很好的改变方法签名,将使的正是你的第一个代码工作-但你可以改变你如何调用该方法只是一点点,使其工作,通过指定lambda表达式的参数类型:

var list = new List<int>();
list.WithEach((int x) => Console.WriteLine(x++))
    .OrderBy(x => x) 
    .WithEach((int x) => Console.WriteLine(x));
Run Code Online (Sandbox Code Playgroud)

当然,它的缺点是它不适用于匿名类型.

这个缺点的一个解决方法是非常可怕的,但它可以让你T在需要时通过参数来表达类型.您将方法签名更改为:

public static TEnumerable WithEach<TEnumerable, T>(
    this TEnumerable items,
    Action<T> action,
    T ignored = default(T))
Run Code Online (Sandbox Code Playgroud)

如果你想用一些匿名类型的列表调用该方法,你可以写:

list.WithEach(x => Console.WriteLine(x.Name), new { Name = "", Value = 10 });
Run Code Online (Sandbox Code Playgroud)

...最终参数将与匿名类型匹配.这将允许T最终参数而不是第二个参数推断出类型.您可以将其用于其他类型的课程,但我可能会坚持将其用于匿名类型.

这真是一个非常可怕的黑客,我不认为我真的使用它,但如果你真的,真的需要这个与匿名类型一起工作,它会应付.

  • 你的信念当然是正确的.基本上**所有推论都必须从形式的参数**,然后*然后*约束进行检查.我们永远不会从约束*做出推断*.我们从参数*进行推理*,然后*验证*它们与约束一致. (8认同)
  • @EricLippert那是有道理的.不胜感激! (2认同)
  • 在推断委托类型的形式参数列表中的类型参数的情况下,该规则 - 形式的参数 - 略微放宽.在这种情况下,正如您正确指出的那样,我们从*正式参数列表中推断出类型化的lambdas*到*具有不固定类型参数*的委托的正式参数列表. (2认同)
  • @MichaelPuckettII:不客气.无论出于何种原因,这都是一个有争议的决定; 很多人认为应该使用约束来帮助编译器*做出决定*,而不是我所相信的,应该使用约束*让开发人员知道推理何时不清楚我们无法确定正确的答案来自论点*.有关此问题的长期,非生产性辩论,请参阅https://blogs.msdn.microsoft.com/ericlippert/2009/12/10/constraints-are-not-part-of-the-signature/上的评论 (2认同)
  • @MichaelPuckettII:仅供参考,Jon的"可怕的黑客"是一种有时被称为"逐个演员"的技术,因为这种技术的原始用例被用于制作*转换*,它采用了你想要转换为的类型的*示例*.https://blogs.msdn.microsoft.com/alexj/2007/11/22/t-castbyexampletobject-ot-example/ (2认同)