2013-02-16 3 views
8

Suche:Wann wird IEnumerable.GetEnumerator anstelle von IEnumerable <T> .GetEnumerator aufgerufen? Bei diesem Code

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

Running:

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

Renditen

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

Ich verstehe, dass die nicht-generische IEnumerator Version für die Kompatibilität ist.

Frage:

  1. In welchem ​​Szenario wird die nicht-generic geltend gemacht werden?
  2. Wie kann ich erzwingen mein Code mit dem nicht-generischen IEnumerator ausgeführt werden?
+0

'Enumerable.Cast' – CodesInChaos

Antwort

3

Die anderen Antworten irgendwie den Punkt verfehlen.

Interfaces keine Rolle überhaupt, wenn die (kompilieren Zeit) Art das, was foreach ed wird gerade eine public nicht-generische nicht-statische Methode GetEnumerator die Null Argumente annimmt genannt hat. (Der Rückgabetyp dieser Methode kann alles sein, egal ob generisch oder nicht-generisch: Schnittstellen spielen keine Rolle.)

Der Grund, warum die erste Ihrer Methoden heißt, ist, dass dies die public Methode ist.

Sie können das ändern:

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

Um zu beweisen, dass Schnittstellen nicht benötigt werden, versuchen Sie dies:

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

Allerdings ist es ratsam, IEnumerable<> zu implementieren. Dann kann Ihr Typ mit Linq (Erweiterungsmethoden auf IEnumerable<>) verwendet werden und kann als Argument für andere Methoden verwendet werden, die einfach eine IEnumerable<> erfordern.

Auch ist es ratsam, die Methode (oder explizite Schnittstellenimplementierung), die den nicht generischen IEnumerator zurückgibt, nur zu der Methode (oder der expliziten Schnittstellenimplementierung) zurückzurufen, die IEnumerator<> zurückgibt. Die zwei unterschiedlichen Sequenzen zu haben ist wirklich verwirrend (aber schön, um Fragen zu stellen und zu beantworten, wie die Dinge funktionieren).

+0

Wenn Sie versuchen zu zeigen, dass Schnittstellen nicht benötigt werden, I ' d schlägt vor, eine Struktur zu implementieren, die eine 'Current'-Eigenschaft und eine' MoveNext'-Methode hat, aber keine Schnittstellen implementiert, und die 'GetEnumerator'-Methode 'myWords' gibt diesen Typ anstelle von' IEnumerable' zurück. – supercat

+0

OK, "eher als' IEnumerator' "meinst du, denke ich. Was ich versuchte zu sagen, war, dass der C# -Compiler für die 'public'-Methode unter den Bedingungen, die ich angegeben habe, gehen würde, also ob es Interfaces gibt und worauf die Interfaces mappen, spielt keine Rolle. Ich sagte nicht, dass die "foreach" wie beabsichtigt/erwartet fortfahren würde. Ich bin dabei, deinen Vorschlag zu versuchen. –

+0

@supercat Was ich schrieb, arbeitete. Dies: 'Klasse DoMe { öffentliche Badge GetEnumerator() { Rückkehr Standard (BadE); } } Struct Bad { ushort Staat; öffentliche bool MoveNext() { ++ Zustand; Rückkehr wahr; } public string Aktuelle { erhalten { return arr [state% 4]; } } statisch readonly string [] arr = {"a", "b", "c", "d",}; } 'Erstellen eine neue' DoMe' und 'foreach' es funktioniert. Was habe ich gezeigt? –

7

Der nicht-generische IEnumerator ausgeführt werden, wenn Code die Klasse der nicht-generische Schnittstelle wirft:

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

Dies ist vor allem dann relevant, wenn Sie Ihre Klasse zu einem gewissen Legacy-Funktion übergeben, die eine nicht erfordert -generischer IEnumerator.

Also, wenn Sie eine Bibliothek haben, die eine Funktion enthält, und Sie geben Ihre Klasse zu dieser Funktion, wird es die nicht-generische 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()); 


Hinweis verwenden, dass es durchaus möglich, die einfach umzusetzen nicht-generische IEnumerator die generische Verwendung:

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

Auf diese Weise können Sie sicher sein, dass sie beide die gleichen Wirkungen haben.

+1

Oder, um ein Beispiel mit' foreach' zu geben: 'foreach (Objektwort in (IEnumerable) myWords) ...' – stakx

4

Die nicht generische Version des IEnumerable ist mit einer expliziten Schnittstellenimplementierung implementiert. Dies bedeutet, dass Sie die explizit implementierte Funktion nur aufrufen können, indem Sie auf die Schnittstelle werfen.

Der Grund IEnumerable ist explizit implementiert, dass die Methodensignaturen bis auf den Rückgabetyp identisch sind.

Casting myWords explizit auf IEnumerable können Sie die nicht-generische Version wie folgt aufrufen: (IEnumerable)myWords.

Der C# Guide erklärt, wie dies funktioniert: Explicit interface implementation.

Verwandte Themen