我怎样才能 return <TEnumerable, T> : where TEnumerable:IEnumerable<T>

How can I return <TEnumerable, T> : where TEnumerable:IEnumerable<T>

目标:泛型枚举类型与 returned 时的类型相同。

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

List<T> 然后 return List<T>

IOrderedEnumerable<T> 然后 return 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;
}

仅示例

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

让它工作

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));

我缺少的是为什么 C# 无法推断类型,尽管 where 过滤器确实使类型准确。我理解您为什么要为方法提供全部或 none 泛型类型,因此请不要向我指出这些答案。

编辑:如果我无法推断类型;那我怎样才能让它更优雅?

仅使用 T 声明您的扩展,如下所示:

public static IEnumerable<T> WithEach<T>(this IEnumerable<T> items,Action<T> action)
{
    foreach (var item in items) action.Invoke(item);
    return items;
}

这样做的缺点是会丢失您实现的 IEnumerable 的特定子class。

很容易为您关心的特定子class实现重载:

public static IOrderedEnumerable<T> WithEach<T>(this IOrderedEnumerable<T> items, Action<T> action)
{
    ((IEnumerable<T>)items).WithEach(action);
    return items;
} 

不过,迭代后返回 IEnumerable 有点可怕。 IEnumerables 可能无法重新启动。

C# 中的类型推断 非常 复杂 - 就这一次,我不会拿出规范来尝试逐步完成它,因为我知道它会变得多么可怕。

相信问题是parameter/argument组合都没有给编译器足够的信息来推断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));

缺点当然是它不适用于匿名类型。

该缺点的一种解决方法非常糟糕,但它可以让您在需要时通过参数来表达 T 的类型。您将方法签名更改为:

public static TEnumerable WithEach<TEnumerable, T>(
    this TEnumerable items,
    Action<T> action,
    T ignored = default(T))

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

list.WithEach(x => Console.WriteLine(x.Name), new { Name = "", Value = 10 });

... 最后一个参数将匹配匿名类型。这将允许 T 的类型由最后一个参数而不是第二个参数推断出来。您当然可以将其用于其他类型,但我可能会坚持将其用于匿名类型。

这是一个非常可怕的 hack,我不认为我 实际上 使用它,但如果你真的,真的需要它来处理匿名类型,它会应付的。