Определить метод расширения для IEnumerable <T>, который возвращает IEnumerable <T>? - PullRequest
22 голосов
/ 10 ноября 2008

Как определить метод расширения для IEnumerable<T>, который возвращает IEnumerable<T>? Цель состоит в том, чтобы сделать метод расширения доступным для всех IEnumerable и IEnumerable<T>, где T может быть анонимным типом.

Ответы [ 3 ]

38 голосов
/ 10 ноября 2008

Самый простой способ написать любой итератор - использовать блок итератора, например:

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

Re 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);
}
4 голосов
/ 10 ноября 2008
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;
            }
        }
    }
}
0 голосов
/ 10 ноября 2008

Этот пост может помочь вам начать: Как вы пишете метод расширения C # для класса с типизированным типом . Я не уверен, что это именно то, что вы ищете, но это может помочь вам начать.

...