2008-11-10 21 views

Odpowiedz

33

Najłatwiej napisać dowolny iterator jest z bloku iteracyjnej, na przykład:

static IEnumerable<T> Where<T>(this IEnumerable<T> data, Func<T, bool> predicate) 
{ 
    foreach(T value in data) 
    { 
     if(predicate(value)) yield return value; 
    } 
} 

Kluczem tutaj jest „yield return”, który zamienia metody w bloku iteracyjnej, ze kompilator generując moduł wyliczający (IEnumerator<T>), który robi to samo. Po wywołaniu, ogólne typ wnioskowanie obsługuje T automatycznie, więc wystarczy:

int[] data = {1,2,3,4,5}; 
var odd = data.Where(i=>i%2 != 0); 

Powyższe może być używany z anonimowych typów dobrze.

Można, Coure określ T jeśli chcesz (tak długo, jak nie jest anonimowy):

var odd = data.Where<int>(i=>i%2 != 0); 

Re IEnumerable (non-generic), dobrze, najprostszym rozwiązaniem jest dla program wywołujący, aby najpierw użyć .Cast<T>(...) lub .OfType<T>(...), aby uzyskać IEnumerable<T>. Możesz przekazać w powyższym numerze this IEnumerable, ale osoba wywołująca będzie musiała sama określić wartość T, zamiast wnioskować o tym kompilator. Nie można tego używać z T jako typ anonimowy, więc morał tutaj jest następujący: nie używaj nietypowej formy IEnumerable z typami anonimowymi.

Istnieje kilka bardziej złożonych scenariuszy, w których sygnatura metody jest taka, że ​​kompilator nie może zidentyfikować T (i oczywiście nie można jej określić dla typów anonimowych). W takich przypadkach zwykle możliwe jest ponowne uwzględnienie w innym sygnaturze, którą kompilator może zastosować z wnioskiem (być może za pomocą metody pass-thru), ale trzeba podać aktualny kod, aby podać tutaj odpowiedź.


(aktualizacja)

Po dyskusji, oto sposób na wykorzystanie Cast<T> z anonimowych typów. Kluczem jest podanie argumentu, który może być użyty do wnioskowania o typ (nawet jeśli argument nie jest nigdy używany). Na przykład:

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); 
} 
+0

metodę z parametrem, który nie jest używany tylko wydaje się błędne. –

+2

It * is * used .... tylko przez kompilator, a nie przez środowisko wykonawcze; - –

+1

Jest używany i dostarczany, ale nie jawny, a raczej jest wywiedziony ... Bez kompilatora C# jest "tak inteligentny" być zmuszonym do wyraźnego określenia go. –

2
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

To jest buforowany iterator; zazwyczaj lepiej byłoby zamiast tego używać bloku iteratora - np. upuszczać listę "List " i po prostu "zwracać element" zamiast Dodaj. –

+1

(dla kontekstu, pierwotna wersja miała listę z.Dodaj, kończąc w zamian; Wolę zaktualizowaną wersję ;-p) –

Powiązane problemy