如何为 IEnumerable<T>
定义扩展方法返回 IEnumerable<T>
?
目标是使扩展方法可用于所有 IEnumerable
和 IEnumerable<T>
其中 T
可以是匿名类型。
最佳答案
编写任何迭代器的最简单方法是使用迭代器 block ,例如:
static IEnumerable<T> Where<T>(this IEnumerable<T> data, Func<T, bool> predicate)
{
foreach(T value in data)
{
if(predicate(value)) yield return value;
}
}
这里的关键是“yield return
”,它把方法变成一个迭代器 block ,编译器生成一个枚举器(IEnumerator<T>
)做同样的事情。调用时,泛型类型推断处理 T
自动,所以你只需要:
int[] data = {1,2,3,4,5};
var odd = data.Where(i=>i%2 != 0);
以上可以很好地与匿名类型一起使用。
当然,您可以指定 T
如果你愿意(只要它不是匿名的):
var odd = data.Where<int>(i=>i%2 != 0);
回复 IEnumerable
(非通用),嗯,最简单的方法是让调用者使用 .Cast<T>(...)
或 .OfType<T>(...)
得到一个IEnumerable<T>
第一的。可以传入this IEnumerable
在上面,但调用者必须指定 T
他们自己,而不是让编译器推断它。你不能将它与 T
一起使用作为匿名类型,所以这里的原则是:不要使用 IEnumerable
的非泛型形式匿名类型。
有一些稍微复杂的场景,其中方法签名使得编译器无法识别 T
(当然你不能为匿名类型指定它)。在这些情况下,通常可以重构为编译器可以用于推理的不同签名(可能通过直通方法),但您需要将实际代码发布到在这里提供答案。
(更新)
经过讨论,这是一种利用 Cast<T>
的方法与匿名类型。关键是提供一个可用于类型推断的参数(即使从未使用过该参数)。例如:
static void Main()
{
IEnumerable data = new[] { new { Foo = "abc" }, new { Foo = "def" }, new { Foo = "ghi" } };
var typed = data.Cast(() => new { Foo = "never used" });
foreach (var item in typed)
{
Console.WriteLine(item.Foo);
}
}
// note that the template is not used, and we never need to pass one in...
public static IEnumerable<T> Cast<T>(this IEnumerable source, Func<T> template)
{
return Enumerable.Cast<T>(source);
}
关于c# - 为返回 IEnumerable<T> 的 IEnumerable<T> 定义扩展方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/277150/