2013-02-16 8 views
8

Patrząc na ten kod:Kiedy IEnumerable.GetEnumerator zostanie wywołany zamiast IEnumerable <T> .GetEnumerator?

public class myWords : IEnumerable<string> 
{ 
    string[] f = "I love you".Split(new string[]{"lo"},StringSplitOptions.RemoveEmptyEntries); 

    public IEnumerator<string> GetEnumerator() 
    { 
     return f.Select(s => s + "2").GetEnumerator(); 
    } 

    IEnumerator IEnumerable.GetEnumerator() 
    { 
     return f.Select(s => s + "3").GetEnumerator(); 
    } 
} 

Bieg:

myWords m = new myWords(); 
foreach (var s in m) 
{ 
    Console.WriteLine(s); 
} 

Plony

I 2 
ve you2 // notice "2", so the generic Ienumerator has executed. 

Rozumiem, że non-generic IEnumerator wersja jest dla kompatybilności.

Pytanie:

  1. W jaki scenariusz będzie nierodzajową powoływać?
  2. Jak mogę wymusić mój kod do uruchomienia z nietypowym IEnumerator?
+0

'Enumerable.Cast' – CodesInChaos

Odpowiedz

3

Inne odpowiedzi nie mają znaczenia.

Interfejsy nie mają znaczenia w ogóle, jeśli (w czasie kompilacji) rodzaj, co jest bardzo dobrym foreach ed ma public nierodzajową metody non-statyczną o nazwie GetEnumerator który zaczyna zera argumentów. (Typ powrotu tej metody może być dowolny, ogólny lub nietypowy: interfejsy nie będą miały znaczenia.)

Dlatego pierwszą z twoich metod nazywa się to, że jest to metoda public.

Można to zmienić:

public class myWords : IEnumerable<string> 
{ 
    string[] f = "I love you".Split(new string[]{"lo"},StringSplitOptions.RemoveEmptyEntries); 

    IEnumerator<string> IEnumerable<string>.GetEnumerator() 
    { 
     return f.Select(s => s + "2").GetEnumerator(); 
    } 

    public IEnumerator GetEnumerator() 
    { 
     return f.Select(s => s + "3").GetEnumerator(); 
    } 
} 

Aby udowodnić, że interfejsy nie są potrzebne, spróbuj tego:

public class myWords // no interfaces! 
{ 
    string[] f = "I love you".Split(new string[]{"lo"},StringSplitOptions.RemoveEmptyEntries); 

    public IEnumerator GetEnumerator() 
    { 
     return f.Select(s => s + "3").GetEnumerator(); 
    } 
} 

Jednak dobrze jest wdrożyć IEnumerable<>. Następnie Twój typ może być użyty z Linq (metody rozszerzające na IEnumerable<>) i może być używany jako argument do innych metod, które po prostu wymagają IEnumerable<>.

Warto również mieć metodę (lub jawną implementację interfejsu), która zwraca nietypowe IEnumerator po prostu wywołanie metody (lub jawnej implementacji interfejsu), która zwraca IEnumerator<>. Posiadanie dwóch różnych sekwencji jest naprawdę mylące (ale przyjemne w zadawaniu pytań i odpowiadaniu na pytania o to, jak działają).

+0

Jeśli próbujesz pokazać, że interfejsy nie są potrzebne, ja ' d sugeruje implementację struktury, która ma własność 'Current' i' MoveNext', ale nie implementuje żadnych interfejsów i ma 'myWords'' 'GetEnumerator' zwracającą ten typ zamiast' IEnumerable'. – supercat

+0

OK, "raczej niż" IEnumerator "" masz na myśli, jak sądzę. Próbowałem powiedzieć, że kompilator C# zastosuje metodę 'public' w warunkach, które określiłem, więc nie będzie miało znaczenia, czy istnieją interfejsy, i na jakie interfejsy się mapują. Nie mówiłem, że "foreach" będzie przebiegać zgodnie z założeniami/oczekiwaniami. Mam zamiar wypróbować twoją sugestię. –

+0

@supercat Co napisałem, działało. To: 'klasa DoMe { publiczny BadE GetEnumerator() { domyślny zwrot (BadE); } } struct BadE { stan ushort; public bool MoveNext() { ++ stan; return true; } public string Current { get { return arr [stan% 4]; } } Statyczny ciąg znaków tylko do odczytu [] arr = {"a", "b", "c", "d",}; } 'Tworzenie nowego' DoMe' i 'foreach'ing to działa. Co pokazałem? –

7

Non-generic IEnumerator zostanie wykonana, gdy kod rzuca klasę do interfejsu non-Generic:

((IEnumerable)myWords).GetEnumerator(); // this calls the non-generic one 

Jest to głównie istotne, jeśli zdać klasę do niektórych funkcji starszego typu, który wymaga non -energetyczny IEnumerator.

Więc jeśli masz jakieś biblioteki, która zawiera funkcję i zdać klasę do tej funkcji, będzie korzystał z nierodzajową IEnumerator

DoSomeStuffWithAnIEnumerable(IEnumerable x) 
{ 
    var foo = x.GetEnumerator(); 

    // or, as stackx said, an example with foreach: 
    foreach (var foo2 in x) 
    Console.WriteLine(foo2); 
} 


DoSomeStuffWithAnIEnumerable(new myWords()); 


Należy pamiętać, że jest to całkowicie poprawny po prostu wdrożyć nietypowy IEnumerator używający ogólnego:

public class myWords : IEnumerable<string> 
{ 
    ....  
    IEnumerator IEnumerable.GetEnumerator() 
    { 
     return this.GetEnumerator(); 
    } 
} 

W ten sposób możesz mieć pewność, że oba mają takie same efekty.

+1

Lub, aby podać przykład używając' foreach': 'foreach (słowo obiektu w (IEnumerable) myWords) ...' – stakx

4

Nietypowa wersja IEnumerable jest implementowana z jawną implementacją interfejsu. Oznacza to, że można wywoływać tylko jawnie zaimplementowaną funkcję, przesyłając do interfejsu.

Powodem, dla którego IEnumerable implementuje się jawnie, jest to, że sygnatury metod są takie same, z wyjątkiem typu zwracanego.

Przesyłanie myWords jawnie na IEnumerable pozwala na wywołanie wersji nietypowej w następujący sposób: (IEnumerable)myWords.

Przewodnik C# wyjaśnia, jak to działa: Explicit interface implementation.

Powiązane problemy