2016-05-31 5 views
22

Ich war mit yield und IEnumerable Herumspielen und ich bin jetzt gespannt, warum oder wie das folgende Snippet funktioniert:Wie ist die Ausbeute aufzählbar?

public class FakeList : IEnumerable<int> 
{ 
    private int one; 
    private int two; 

    public IEnumerator<int> GetEnumerator() 
    { 
     yield return one; 
     yield return two; 
    } 

    IEnumerator IEnumerable.GetEnumerator() 
    { 
     return GetEnumerator(); 
    } 
} 

Nun, wie sich dies der Compiler drehen:

public IEnumerator<int> GetEnumerator() 
{ 
    yield return one; 
    yield return two; 
} 

in ein IEnumerator<int>?

+3

http://csharpindepth.com/Articles/Chapter6/IteratorBlockImplementation.aspx –

+1

Compiler Magie! – RBarryYoung

Antwort

27

Wenn yield return verwendet wird, erzeugt der Compiler einen Enumerator-Klasse für Sie. Der eigentliche Code, der verwendet wird, ist also viel komplexer als nur zwei Return-Anweisungen. Der Compiler fügt alle notwendigen Code einen Enumerator für Sie zurückkommen, die iteriert über die Ergebnisse der yield return.


Dieser der generierte Code aus Ihrer FakeList.GetEnumerator():

using System; 
using System.Collections; 
using System.Collections.Generic; 
using System.Diagnostics; 
using System.Runtime.CompilerServices; 

public class FakeList : IEnumerable<int>, IEnumerable 
{ 
    private int one; 
    private int two; 

    [IteratorStateMachine(typeof(<GetEnumerator>d__2))] 
    public IEnumerator<int> GetEnumerator() 
    { 
     yield return this.one; 
     yield return this.two; 
    } 

    IEnumerator IEnumerable.GetEnumerator() => 
     this.GetEnumerator(); 

    [CompilerGenerated] 
    private sealed class <GetEnumerator>d__2 : IEnumerator<int>, IDisposable, IEnumerator 
    { 
     private int <>1__state; 
     private int <>2__current; 
     public FakeList <>4__this; 

     [DebuggerHidden] 
     public <GetEnumerator>d__2(int <>1__state) 
     { 
      this.<>1__state = <>1__state; 
     } 

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

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

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

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

     [DebuggerHidden] 
     void IDisposable.Dispose() 
     { 
     } 

     int IEnumerator<int>.Current => 
      this.<>2__current; 

     object IEnumerator.Current => 
      this.<>2__current; 
    } 
} 

Sehen Sie die <GetEnumerator>d__2 Klasse? Das wird basierend auf Ihren zwei yield return s generiert.

12

Wenn der Compiler yield return oder yield break sieht, übernimmt er die Funktion und wandelt die Logik in eine Klasse um, die eine Zustandsmaschine implementiert. Eine Instanz dieser Klasse wird dann zurückgegeben, wenn die Methode aufgerufen wird.

C# In Depth hat einen Abschnitt über, was der Code aussieht.

5

Es ist ein Generator. Ich kann nicht sagen, wie der compilator hinter funktioniert, aber wenn Sie das tun:

yield return x; 

Sie die Funktion wie die klassische Rückkehr nicht verlassen, sondern kehren Sie Wert und dann die Ausführung der Funktion fortzusetzen.

Wenn ich mich gut erinnere, gibt es einen kleinen Unterschied zwischen einem echten aufzählbar und dieser, wenn Sie nicht eine Methode verwenden, um Ihren Generator in eine echte aufzählbar zu verwandeln (je nachdem, was Sie erreichen möchten) können Sie habe ein paar Probleme.

+0

Können Sie den Unterschied erweitern? Weil andere mentoning dass der Compiler eine zählbare Implementierung erzeugt – Mafii

+0

Ich werde versuchen, es wieder zu finden. –

+0

hast du etwas gefunden? – Mafii