2010-12-15 8 views
6

Ok, wie ich mit dem Aufbau einer benutzerdefinierten enumerator wurde Stochern, hatte ich dieses Verhalten bemerkt, dass die Ausbeute betrifftDie Wunder der Ausbeute Schlüsselwort

Sagen Sie so etwas wie dieses:

public class EnumeratorExample 
    { 

     public static IEnumerable<int> GetSource(int startPoint) 
     { 
       int[] values = new int[]{1,2,3,4,5,6,7}; 
       Contract.Invariant(startPoint < values.Length); 
       bool keepSearching = true; 
       int index = startPoint; 

       while(keepSearching) 
       { 
         yield return values[index]; 
         //The mind reels here 
         index ++ 
         keepSearching = index < values.Length; 
       } 
     } 

    } 

Was macht es unter der Haube des Compilers möglich, den Index ++ und den Rest des Codes in der while-Schleife auszuführen, nachdem Sie technisch von der Funktion zurückgekehrt sind?

Antwort

9

Der Compiler schreibt den Code in eine Zustandsmaschine um. Die einzelne Methode, die Sie geschrieben haben, ist in verschiedene Teile aufgeteilt. Jedes Mal, wenn Sie MoveNext (entweder implicity oder explizit) aufrufen, wird der Status erweitert und der richtige Code-Block wird ausgeführt.

Empfohlene wenn Sie weitere Informationen wünschen Lesen wissen:

+0

Ja, ok, Staatsmaschine, das habe ich gelesen. Aber was für einen Code erzeugt er und was macht die Zustandsmaschine damit? Pseudocode würde sehr geschätzt werden. – dexter

+0

@Max Malygin: Der Artikel, den ich verlinkt habe, http://csharpindepth.com/Articles/Chapter6/IteratorBlockImplementation.aspx zeigt den Code, der generiert wird. –

+0

@Mark, cool, danke, ich werde die auschecken! – dexter

2

Ausbeute ist Magie.

Nun, nicht wirklich. Der Compiler generiert eine vollständige Klasse zum Generieren der von Ihnen ausgeführten Aufzählung. Es ist im Grunde Zucker, um dein Leben einfacher zu machen.

Lesen Sie this für ein Intro.

EDIT: Falsch dies. Der Link wurde geändert. Überprüfen Sie erneut, ob Sie das einmal getan haben.

+0

danke, das hilft ein bisschen, also + – dexter

+0

@Max - Je nachdem, wann Sie auf den Link geklickt haben, kann es jetzt anders sein. Ich habe ursprünglich das falsche gepostet. – Donnie

4

Der Compiler generiert in Ihrem Auftrag eine Zustandsmaschine.

Von der Sprachspezifikation:

10,14 Iteratoren

10.14.4 Enumerator-Objekte

Wenn ein Funktionselement eines enumerator Schnittstelle Typs Rückkehr ist unter Verwendung eines Iteratorblock umgesetzt, Aufruf der Funktion Mitglied nicht sofort ausführen den Code in der Iteratorblock. Stattdessen wird ein Enumerator Objekt erstellt und zurückgegeben. Dieses -Objekt kapselt den angegebenen Code im Iteratorblock ein, und die Ausführung des Codes im Iteratorblock tritt auf, wenn die MoveNext-Methode des Enumeratorobjekts aufgerufen wird.Ein Enumeratorobjekt hat die folgenden Eigenschaften :

• Es implementiert IEnumerator und IEnumerator, wobei T die Ausbeute Typ des Iterators ist.

• Es implementiert System.IDisposable.

• Es wird mit einer Kopie der Argumentwerte (falls vorhanden) und der Instanz Wert initialisiert, der an das Funktionsmember übergeben wird.

• Es hat vier mögliche Zustände, vor, läuft, ausgesetzt, und nach, und ist zunächst im Zustand vor.

Ein Enumeratorobjekt ist typischerweise eine Instanz eines Compiler generierte Enumerator Klasse, die den Code in dem Iteratorblock kapselt und implementiert den Enumerator Schnittstellen, aber andere Methoden der Implementierung möglich sind. Wenn eine Enumerator-Klasse vom Compiler generiert wird, wird diese Klasse verschachtelt werden, die direkt oder indirekt, in der Klasse das Funktionselement enthält, wird es privaten Zugang hat, und es wird hat einen Namen für Compiler reserviert (§2.4.2).

Um eine Vorstellung von dieser zu erhalten, ist hier, wie Reflector Klasse dekompiliert:

public class EnumeratorExample 
{ 
    // Methods 
    public static IEnumerable<int> GetSource(int startPoint) 
    { 
     return new <GetSource>d__0(-2) { <>3__startPoint = startPoint }; 
    } 

    // Nested Types 
    [CompilerGenerated] 
    private sealed class <GetSource>d__0 : IEnumerable<int>, IEnumerable, IEnumerator<int>, IEnumerator, IDisposable 
    { 
     // Fields 
     private int <>1__state; 
     private int <>2__current; 
     public int <>3__startPoint; 
     private int <>l__initialThreadId; 
     public int <index>5__3; 
     public bool <keepSearching>5__2; 
     public int[] <values>5__1; 
     public int startPoint; 

     // Methods 
     [DebuggerHidden] 
     public <GetSource>d__0(int <>1__state) 
     { 
      this.<>1__state = <>1__state; 
      this.<>l__initialThreadId = Thread.CurrentThread.ManagedThreadId; 
     } 

     private bool MoveNext() 
     { 
      switch (this.<>1__state) 
      { 
       case 0: 
        this.<>1__state = -1; 
        this.<values>5__1 = new int[] { 1, 2, 3, 4, 5, 6, 7 }; 
        this.<keepSearching>5__2 = true; 
        this.<index>5__3 = this.startPoint; 
        while (this.<keepSearching>5__2) 
        { 
         this.<>2__current = this.<values>5__1[this.<index>5__3]; 
         this.<>1__state = 1; 
         return true; 
        Label_0073: 
         this.<>1__state = -1; 
         this.<index>5__3++; 
         this.<keepSearching>5__2 = this.<index>5__3 < this.<values>5__1.Length; 
        } 
        break; 

       case 1: 
        goto Label_0073; 
      } 
      return false; 
     } 

     [DebuggerHidden] 
     IEnumerator<int> IEnumerable<int>.GetEnumerator() 
     { 
      EnumeratorExample.<GetSource>d__0 d__; 
      if ((Thread.CurrentThread.ManagedThreadId == this.<>l__initialThreadId) && (this.<>1__state == -2)) 
      { 
       this.<>1__state = 0; 
       d__ = this; 
      } 
      else 
      { 
       d__ = new EnumeratorExample.<GetSource>d__0(0); 
      } 
      d__.startPoint = this.<>3__startPoint; 
      return d__; 
     } 

     [DebuggerHidden] 
     IEnumerator IEnumerable.GetEnumerator() 
     { 
      return this.System.Collections.Generic.IEnumerable<System.Int32>.GetEnumerator(); 
     } 

     [DebuggerHidden] 
     void IEnumerator.Reset() 
     { 
      throw new NotSupportedException(); 
     } 

     void IDisposable.Dispose() 
     { 
     } 

     // Properties 
     int IEnumerator<int>.Current 
     { 
      [DebuggerHidden] 
      get 
      { 
       return this.<>2__current; 
      } 
     } 

     object IEnumerator.Current 
     { 
      [DebuggerHidden] 
      get 
      { 
       return this.<>2__current; 
      } 
     } 
    } 
}