如何为IEnumerable<T>
定义一个返回IEnumerable<T>
的扩展方法?
目标是使扩展方法适用于所有IEnumerable
和IEnumerable<T>
,其中T
可以是匿名类型。
答案 0 :(得分:38)
编写任何迭代器的最简单方法是使用迭代器块,例如:
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
”,它将方法转换为迭代器块,编译器生成一个同样的枚举器(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
(当然,您无法为匿名类型指定它)。在这些情况下,通常可以重新考虑编译器可以与推理一起使用的不同签名(可能通过pass-thru方法),但是您需要将实际代码发布到在这里提供答案。
(更新)
在讨论之后,这里有一种方法可以将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);
}
答案 1 :(得分:4)
using System;
using System.Collections.Generic;
namespace ExtentionTest {
class Program {
static void Main(string[] args) {
List<int> BigList = new List<int>() { 1,2,3,4,5,11,12,13,14,15};
IEnumerable<int> Smalllist = BigList.MyMethod();
foreach (int v in Smalllist) {
Console.WriteLine(v);
}
}
}
static class EnumExtentions {
public static IEnumerable<T> MyMethod<T>(this IEnumerable<T> Container) {
int Count = 1;
foreach (T Element in Container) {
if ((Count++ % 2) == 0)
yield return Element;
}
}
}
}
答案 2 :(得分:0)
这篇文章可以帮助您入门:How do you write a C# Extension Method for a Generically Typed Class。我不确定它是不是你想要的,但它可能会让你开始。