为IEnumerable<T>定义一个扩展方法,该方法返回IEnumerable<T>。

35
我如何为IEnumerable<T>定义一个扩展方法,它返回IEnumerable<T>
目标是使扩展方法适用于所有IEnumerableIEnumerable<T>,其中T可以是匿名类型。
3个回答

54

编写任何迭代器最简单的方法是使用迭代器块,例如:

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,而不能让编译器推断。对于匿名类型,你不能使用此方法,因此结论是:不要在匿名类型中使用非泛型的 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);
}

1
它被使用和提供,但不是明确地,而是被推断出来的... 如果没有C#编译器“如此聪明”,我们将被迫明确指定它。 - user3638471

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

那是一个缓冲迭代器;通常最好使用迭代器块 - 即放弃 List<T>,而是只需使用 "yield return 元素" 而不是 Add。 - Marc Gravell
1
(为了更好的上下文,原始版本中有一个List<T>,使用.Add方法,最后以return结束;我更喜欢更新后的版本 ;-p)以下是翻译内容:以下是更新后的版本: - Marc Gravell

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接