2010-05-03 2 views

Antwort

13

Ertrag Implementierung erreicht den Code nicht, bis es benötigt wird.

Zum Beispiel dieser Code:

public IEnumerable<int> GetInts() 
{ 
    yield return 1; 
    yield return 2; 
    yield return 3; 
} 

tatsächlich in eine verschachtelte Klasse kompilieren, die IEnumerable<int> implementiert und der Körper von GetInts() wird eine Instanz dieser Klasse zurück.

Reflektor Sie sehen können:

public IEnumerable<int> GetInts() 
{ 
    <GetInts>d__6d d__d = new <GetInts>d__6d(-2); 
    d__d.<>4__this = this; 
    return d__d; 
} 

bearbeiten - weitere Informationen über GetInts Implementierung hinzu:
Die Art und Weise diese Implementierung es faul macht basiert auf dem EnumeratorMoveNext() Methode. Wenn die aufzählbare verschachtelte Klasse generiert wird (<GetInts>d__6d im Beispiel), hat sie einen Status und für jeden Status ist ein Wert verbunden (dies ist ein einfacher Fall, in fortgeschritteneren Fällen wird der Wert ausgewertet, wenn der Code den Status erreicht). Wenn wir einen Blick in dem MoveNext() Code von <GetInts>d__6d nehmen wir den Zustand sehen:

private bool MoveNext() 
{ 
    switch (this.<>1__state) 
    { 
     case 0: 
      this.<>1__state = -1; 
      this.<>2__current = 1; 
      this.<>1__state = 1; 
      return true; 

     case 1: 
      this.<>1__state = -1; 
      this.<>2__current = 2; 
      this.<>1__state = 2; 
      return true; 

     case 2: 
      this.<>1__state = -1; 
      this.<>2__current = 3; 
      this.<>1__state = 3; 
      return true; 

     case 3: 
      this.<>1__state = -1; 
      break; 
    } 
    return false; 
} 

Wenn der Enumerator für das aktuelle Objekt gefragt wird, gibt es das Objekt, das den aktuellen Zustand verbunden ist.

Um zu zeigen, dass der Code nur dann ausgewertet wird, wenn es erforderlich ist man an diesem Beispiel sehen kann:

[TestFixture] 
public class YieldExample 
{ 
    private int flag = 0; 
    public IEnumerable<int> GetInts() 
    { 
     yield return 1; 
     flag = 1; 
     yield return 2; 
     flag = 2; 
     yield return 3; 
     flag = 3; 
    } 

    [Test] 
    public void Test() 
    { 
     int expectedFlag = 0; 
     foreach (var i in GetInts()) 
     { 
      Assert.That(flag, Is.EqualTo(expectedFlag)); 
      expectedFlag++; 
     } 

     Assert.That(flag, Is.EqualTo(expectedFlag)); 
    } 
} 

hoffe, dass ich es ein bisschen mehr klar. Ich empfehle, den Code mit Reflector zu betrachten und den kompilierten Code zu beobachten, wenn Sie den "yield" -Code ändern.

+0

@Elisha: bitte geben Sie weitere Informationen über GetInt(). –

+0

@masoud ramezani, weitere Informationen zur untergeordneten Entiler-Klasse GetInt. – Elisha

+0

Vielen Dank für Ihre vollständige Antwort. –

4

Grundsätzlich unter Verwendung yield Aussagen implementated Iteratoren sind in einer Klasse zusammengestellt, die ein state machine implementiert.

Wenn Sie nie foreach (= iterieren und tatsächlich verwenden) die zurückgegebenen IEnumerable<T>, wird der Code nie tatsächlich ausgeführt. Und wenn Sie das tun, wird nur der minimale Code ausgeführt, der benötigt wird, um den nächsten zurückzugebenden Wert zu bestimmen, nur um die Ausführung fortzusetzen, wenn ein nächster Wert angefordert wird.

Sie können dieses Verhalten tatsächlich beobachten, wenn Sie einen solchen Code im Debugger einzeln ausführen. Probieren Sie es mindestens einmal aus: Ich denke, es ist aufschlussreich, dass dies Schritt für Schritt geschieht.