Frage

Ich würde es lieben mich, um herauszufinden, aber ich habe mich gefragt, in etwa, was der Algorithmus ist eine Funktion mit Ertrag-Anweisungen in einer Zustandsmaschine für einen Enumerator für die Umwandlung von Zum Beispiel, wie funktioniert C # diese drehen?:

IEnumerator<string> strings(IEnumerable<string> args)
 { IEnumerator<string> enumerator2 = getAnotherEnumerator();     
   foreach(var arg in arg) 
    { enumerator2.MoveNext();
      yield return arg+enumerator.Current;
    } 
 }

in diesen:

bool MoveNext()
 { switch (this.state)
    {
        case 0:
            this.state = -1;
            this.enumerator2 = getAnotherEnumerator();
            this.argsEnumerator = this.args.GetEnumerator();
            this.state = 1;
            while (this.argsEnumerator.MoveNext())
            {
                this.arg = this.argsEnumerator.Current;
                this.enumerator2.MoveNext();
                this.current = this.arg + this.enumerator2.Current;
                this.state = 2;
                return true;

              state1:
                this.state = 1;
            }
            this.state = -1;
            if (this.argsEnumerator != null) this.argsEnumerator.Dispose();
            break;

        case 2:
            goto state1;
    }
    return false;
}

Natürlich kann das Ergebnis ganz anders sein, je nach dem ursprünglichen Code.

War es hilfreich?

Lösung

Die besondere Codebeispiel Sie suchen, umfasst eine Reihe von Transformationen. Bitte beachten Sie, dass dies eine ungefähre Beschreibung des Algorithmus. Die tatsächlichen Namen durch den Compiler und den genauen Code verwendet es erzeugt unterschiedlich sein kann. Die Idee ist die gleiche, aber.

Die erste Transformation ist die "foreach" Transformation, die diesen Code transformiert:

foreach (var x in y)
{
   //body
}

in diesem Code:

var enumerator = y.GetEnumerator();
while (enumerator.MoveNext())
{
    var x = enumerator.Current;
    //body
}

if (y != null)
{
    enumerator.Dispose();
}

Die zweite Transformation findet alle yield return-Anweisungen in dem Funktionskörper weist eine Nummer, die jedem (ein Zustand-Wert), und erzeugt eine „goto Bezeichnung“ direkt nach der Ausbeute.

Die dritte Transformation hebt alle lokalen Variablen und Funktionsargumente in dem Verfahren Körper in ein Objekt eine Schließung genannt.

Sie den Code in Ihrem Beispiel gegeben, damit etwa so aussehen würde:

 class ClosureEnumerable : IEnumerable<string>
 {
    private IEnumerable<string> args;
    private ClassType originalThis;
    public ClosureEnumerator(ClassType origThis, IEnumerable<string> args)
    {
        this.args = args;
        this.origianlThis = origThis;
    }
    public IEnumerator<string> GetEnumerator()
    {
        return new Closure(origThis, args);
    }
 }

class Closure : IEnumerator<string>
{
    public Closure(ClassType originalThis, IEnumerable<string> args)
    {
        state = 0;
        this.args = args;
        this.originalThis = originalThis;
    }

    private IEnumerable<string> args;
    private IEnumerator<string> enumerator2;
    private IEnumerator<string> argEnumerator;

    //- Here ClassType is the type of the object that contained the method
    //  This may be optimized away if the method does not access any 
    //  class members
    private ClassType originalThis;

    //This holds the state value.
    private int state;
    //The current value to return
    private string currentValue;

    public string Current
    {
        get 
        {
            return currentValue;
        }
    }
}

Das Verfahren Körper wird dann von dem ursprünglichen Verfahren ein Verfahren inside „Closure“ genannt Movenext, bewegt wird, die eine bool zurückgibt, und implementiert IEnumerable.MoveNext. Jeder Zugriff auf alle Einheimischen wird geleitet durch „dieses“ und jeder Zugriff auf alle Klassenmitglieder werden geleitet durch this.originalThis.

Jedes "yield return expr" Übersetzung:

currentValue = expr;
state = //the state number of the yield statement;
return true;

Jede yield break-Anweisung Übersetzung:

state = -1;
return false;

Es gibt eine „implizite“ yield break-Anweisung am Ende der Funktion. Eine switch-Anweisung wird dann zu Beginn des Verfahrens eingeführt, die an der Zustandszahl aussieht und springt an die zugehörige Etikett.

Die ursprüngliche Methode dann in etwa wie folgt übersetzt:

IEnumerator<string> strings(IEnumerable<string> args)
{
   return new ClosureEnumerable(this,args);
}

Die Tatsache, dass der Zustand des Verfahrens alle in ein Objekt, und dass die Methode Movenext geschoben wird, verwendet eine switch-Anweisung / Zustandsgröße ist, was der Iterator wie verhalten kann, wenn die Steuerung übergeben wird, zurück zu dem Punkt unmittelbar nach dem letzten „yield return“ Anweisung das nächste Mal „Movenext“ bezeichnet wird.

Es ist jedoch wichtig, darauf hinzuweisen, dass die Transformation von der C # Compiler verwendet wird, ist nicht der beste Weg, dies zu tun. Es leidet unter schlechter Leistung bei dem Versuch, „Ausbeute“ mit rekursiven Algorithmen zu verwenden. Es ist ein gutes Papier, das einen besseren Weg skizziert dies hier zu tun:

http://research.microsoft.com/en-us /projects/specsharp/iterators.pdf

Es ist lesenswert, wenn Sie es noch nicht gelesen haben.

Andere Tipps

getupft Gerade diese Frage - ich schrieb kürzlich einen Artikel darauf. Ich werde allerdings die anderen Links hier erwähnt zu dem Artikel hinzufügen muß ...

Raymond chen beantwortet diese; http://blogs.msdn.com/b/ oldnewthing / Archiv / 2008/08/12 / 8849519.aspx

(editierenden Stelle zu Teil 1 der Serie, die nicht Teil 4)

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top