目标:通用枚举类型在返回时是相同类型。
注意:输入类型时此方法有效,但我不明白为什么不能推断出它们。
List<T>
然后返回List<T>
IOrderedEnumerable<T>
然后返回IOrderedEnumerable<T>
ETC
当前方法(仅在输入所有类型时有效)
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));
我所缺少的是,尽管where
过滤器确实使类型准确,但是C#不能推断类型。我了解您为什么不向方法提供全部或不提供泛型类型,所以请不要将我指向这些答案。
编辑:如果我不能推断类型;那我怎样才能使它更优雅呢?
答案 0 :(得分:9)
类型推断非常复杂-仅一次,我不会制定规范尝试逐步进行,因为我知道它可能会多么可怕成为。
我相信问题是,参数/参数组合都不给编译器足够的信息来推断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
的类型。当然,您可以将其用于其他类型,但我可能会坚持将其用于匿名类型。
这都是一个非常可怕的骇客,我不认为我会实际使用它,但是如果您确实需要使用匿名类型,它将可以应对。
>答案 1 :(得分:-1)
仅使用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特定子类的缺点。
很容易为您关心的特定子类实现重载:
public static IOrderedEnumerable<T> WithEach<T>(this IOrderedEnumerable<T> items, Action<T> action)
{
((IEnumerable<T>)items).WithEach(action);
return items;
}
但是,在迭代之后返回IEnumerable有点吓人。 IEnumerables可能无法重新启动。