Was bedeutet "Ertragsbruch"? in C # tun?


494

Ich habe diese Syntax in MSDN gesehen: yield breakaber ich weiß nicht, was es tut. Weiß jemand?


26
In der MSDN-Dokumentation wird dies jedoch erwähnt, jedoch nicht erläutert. Das ist ein schlechter Weg, um einen wissenshungrigen Entwickler zu ärgern.
Phil

3
Die Ertragsrendite macht eine Sicherungsliste überflüssig, dh Sie müssen nicht so etwas wie MyList.Add(...)einfach tun codieren yield return .... Wenn Sie vorzeitig aus der Schleife ausbrechen und die von Ihnen verwendete virtuelle Hintergrundliste zurückgeben müssenyield break;
The Muffin Man

Antworten:


529

Es gibt an, dass ein Iterator beendet wurde. Sie können sich yield breakeine returnAnweisung vorstellen, die keinen Wert zurückgibt.

Wenn Sie beispielsweise eine Funktion als Iterator definieren, sieht der Funktionskörper möglicherweise folgendermaßen aus:

for (int i = 0; i < 5; i++)
{
    yield return i;
}

Console.Out.WriteLine("You will see me");

Beachten Sie, dass nach Abschluss aller Zyklen der Schleife die letzte Zeile ausgeführt wird und die Meldung in Ihrer Konsolen-App angezeigt wird.

Oder so mit yield break:

int i = 0;
while (true)
{
    if (i < 5)
    {
        yield return i;
    }
    else
    {
        // note that i++ will not be executed after this
        yield break;
    }
    i++;
}

Console.Out.WriteLine("Won't see me");

In diesem Fall wird die letzte Anweisung nie ausgeführt, da wir die Funktion vorzeitig verlassen haben.


8
Könnte es einfach breakstatt yield breakin Ihrem obigen Beispiel sein? Der Compiler beschwert sich nicht darüber.
Orad

85
@orad a simple würde breakin diesem Fall die Schleife stoppen, aber die Methodenausführung nicht abbrechen, sodass die letzte Zeile ausgeführt würde und der Text "Ich sehe mich nicht" tatsächlich angezeigt würde.
Damir Zekić

5
@Damir Zekić Können Sie Ihrer Antwort auch hinzufügen, warum Sie eine Renditeunterbrechung der Rendite vorziehen sollten und was die Unterschiede zu beiden sind?
Bruno Costa

11
@ DamirZekić gibt null zurück und Yield Break ist nicht dasselbe. Wenn Sie null zurückgeben, können Sie NullReferenceExceptionden Enumerator von IEnumerable abrufen, während Sie dies bei Yield Break nicht tun (es gibt eine Instanz ohne Elemente).
Bruno Costa

6
@BrunoCosta Der Versuch, regelmäßige Rückgaben in derselben Methode zu erzielen, führt zu einem Compilerfehler. Wenn Sie yield return xden Compiler mit Warnungen versehen, soll diese Methode syntaktischer Zucker zum Erstellen eines EnumeratorObjekts sein. Dieser Enumerator verfügt über Methode MoveNext()und Eigenschaft Current. MoveNext () führt die Methode bis zu einer yield returnAnweisung aus und wandelt diesen Wert in um Current. Beim nächsten Aufruf von MoveNext wird die Ausführung von dort aus fortgesetzt. yield breaksetzt Current auf null und signalisiert das Ende dieses Enumerators, so dass a foreach (var x in myEnum())endet.
Wolfzoon

57

Beendet einen Iteratorblock (z. B. sagt, dass die IEnumerable keine weiteren Elemente enthält).


2
mit [+1] - obwohl es akademisch keine Iteratoren in .NET gibt. nur Aufzähler (eine Richtung, vorwärts.)
Shaun Wilson

@Shaun Wilson, aber mit yieldSchlüsselwort können Sie Sammlung in beide Richtungen iterieren, außerdem können Sie jedes nächste Element der Sammlung nicht in einer Reihe nehmen
monstr

@monstr Sie können die Sammlung auf jede Art und Weise iterieren und müssen es nicht yieldtun. Ich sage nicht, dass Sie falsch liegen, ich sehe, was Sie vorschlagen; Akademisch gesehen gibt es in .NET jedoch keine Iteratoren, nur Enumeratoren (eine Richtung, vorwärts) - im Gegensatz zu stdc ++ ist in der CTS / CLR kein "generisches Iterator-Framework" definiert. LINQ hilft dabei, die Lücke mit Erweiterungsmethoden zu schließen, die yield return auch Rückrufmethoden verwenden. Es handelt sich jedoch um erstklassige Erweiterungsmethoden, nicht um erstklassige Iteratoren. Das Ergebnis IEnumerablekann selbst nicht in eine andere Richtung als die Weiterleitung an den Anrufer iterieren.
Shaun Wilson

29

Sagt dem Iterator, dass das Ende erreicht ist.

Als Beispiel:

public interface INode
{
    IEnumerable<Node> GetChildren();
}

public class NodeWithTenChildren : INode
{
    private Node[] m_children = new Node[10];

    public IEnumerable<Node> GetChildren()
    {
        for( int n = 0; n < 10; ++n )
        {
            yield return m_children[ n ];
        }
    }
}

public class NodeWithNoChildren : INode
{
    public IEnumerable<Node> GetChildren()
    {
        yield break;
    }
}

21

yieldGrundsätzlich IEnumerable<T>verhält sich eine Methode ähnlich wie ein kooperativ (im Gegensatz zu präventiv) geplanter Thread.

yield returnist wie ein Thread, der eine "Zeitplan" - oder "Ruhe" -Funktion aufruft, um die Kontrolle über die CPU aufzugeben. Genau wie bei einem Thread IEnumerable<T>erhält die Methode unmittelbar danach die Steuerelemente zurück, wobei alle lokalen Variablen dieselben Werte haben wie vor der Aufgabe der Steuerung.

yield break ist wie ein Faden, der das Ende seiner Funktion erreicht und endet.

Die Leute sprechen von einer "Zustandsmaschine", aber eine Zustandsmaschine ist alles, was ein "Faden" wirklich ist. Ein Thread hat einen bestimmten Status (dh Werte lokaler Variablen), und jedes Mal, wenn er geplant ist, werden einige Aktionen ausgeführt, um einen neuen Status zu erreichen. Der entscheidende Punkt yieldist, dass im Gegensatz zu den gewohnten Betriebssystem-Threads der Code, der ihn verwendet, rechtzeitig eingefroren wird, bis die Iteration manuell erweitert oder beendet wird.



9

Die yield breakAnweisung bewirkt, dass die Aufzählung gestoppt wird. Tatsächlich yield breakschließt die Aufzählung ohne zusätzliche Einzelteile zurückbringen.

Bedenken Sie, dass es tatsächlich zwei Möglichkeiten gibt, wie eine Iteratormethode die Iteration stoppen kann. In einem Fall könnte die Logik der Methode die Methode natürlich verlassen, nachdem alle Elemente zurückgegeben wurden. Hier ist ein Beispiel:

IEnumerable<uint> FindPrimes(uint startAt, uint maxCount)
{
    for (var i = 0UL; i < maxCount; i++)
    {
        startAt = NextPrime(startAt);
        yield return startAt;
    }

    Debug.WriteLine("All the primes were found.");
}

Im obigen Beispiel stoppt die Iteratormethode natürlich die Ausführung, sobald maxCountPrimzahlen gefunden wurden.

Die yield breakAnweisung ist eine weitere Möglichkeit für den Iterator, die Aufzählung einzustellen. Es ist ein Weg, früh aus der Aufzählung auszubrechen. Hier ist die gleiche Methode wie oben. Dieses Mal ist die Zeit begrenzt, die die Methode ausführen kann.

IEnumerable<uint> FindPrimes(uint startAt, uint maxCount, int maxMinutes)
{
    var sw = System.Diagnostics.Stopwatch.StartNew();
    for (var i = 0UL; i < maxCount; i++)
    {
        startAt = NextPrime(startAt);
        yield return startAt;

        if (sw.Elapsed.TotalMinutes > maxMinutes)
            yield break;
    }

    Debug.WriteLine("All the primes were found.");
}

Beachten Sie den Anruf an yield break. Tatsächlich wird die Aufzählung vorzeitig beendet.

Beachten Sie auch, dass das yield breakanders funktioniert als nur eine Ebene break. Beendet im obigen Beispiel yield breakdie Methode, ohne den Aufruf von zu tätigen Debug.WriteLine(..).


7

Hier ist http://www.alteridem.net/2007/08/22/the-yield-statement-in-c/ ein sehr gutes Beispiel:

public static IEnumerable <int> Bereich (int min, int max)
{
   während (wahr)
   {
      if (min> = max)
      {
         Ertragsbruch;
      }}
      Yield Return min ++;
   }}
}}

und Erklärung, dass, wenn eine yield breakAnweisung innerhalb einer Methode getroffen wird, die Ausführung dieser Methode ohne Rückgabe stoppt. Es gibt einige Zeitsituationen, in denen Sie keine Ertragsunterbrechung verwenden können, wenn Sie kein Ergebnis liefern möchten.


5

Yield Break ist nur eine Möglichkeit, zum letzten Mal Return zu sagen und keinen Wert zurückzugeben

z.B

// returns 1,2,3,4,5
IEnumerable<int> CountToFive()
{
    yield return 1;
    yield return 2;
    yield return 3;
    yield return 4;
    yield return 5;
    yield break;
    yield return 6;
    yield return 7;
    yield return 8;
    yield return 9;
 }

4

Wenn Sie unter "Was bewirkt Ertragsunterbrechung wirklich?" Meinen, "wie funktioniert es?" - Weitere Informationen finden Sie im Blog von Raymond Chen unter https://devblogs.microsoft.com/oldnewthing/20080812-00/?p=21273

C # -Iteratoren generieren sehr komplizierten Code.


12
Nun, sie sind nur kompliziert, wenn Sie sich für den Code interessieren, in den sie kompiliert wurden. Code, der sie verwendet, ist ziemlich einfach.
Robert Rossney

@ Robert, das habe ich gemeint, also habe ich die Antwort aktualisiert, um Ihren Kommentar aufzunehmen
Tony Lee

-2

Das Schlüsselwort yield wird zusammen mit dem Schlüsselwort return verwendet, um dem Enumerator-Objekt einen Wert bereitzustellen. Yield Return gibt den oder die zurückgegebenen Werte an. Wenn die Renditeerklärung erreicht ist, wird der aktuelle Standort gespeichert. Die Ausführung wird an dieser Stelle beim nächsten Aufruf des Iterators neu gestartet.

So erklären Sie die Bedeutung anhand eines Beispiels:

    public IEnumerable<int> SampleNumbers()
    {
        int counter = 0;
        yield return counter;

        counter = counter + 2;

        yield return counter;

        counter = counter + 3;

        yield return counter ;
    }

Wenn dies wiederholt wird, werden folgende Werte zurückgegeben: 0, 2, 5.

Es ist wichtig zu beachten , dass Zähler Variable in diesem Beispiel eine lokale Variable ist. Nach der zweiten Iteration, die den Wert 2 zurückgibt, beginnt die dritte Iteration an der Stelle, an der sie zuvor verlassen wurde, wobei der vorherige Wert der lokalen Variablen mit dem Namen counter beibehalten wird, der 2 war.


11
Sie haben nicht erklärt, was yield breaktut
Jay Sullivan

Ich glaube nicht, dass yield returndie Rückgabe mehrerer Werte unterstützt wird. Vielleicht haben Sie das eigentlich nicht gemeint, aber so habe ich es gelesen.
Sam

Sam - Die SampleNumbers-Methode mit mehreren Yield-Return-Anweisungen funktioniert tatsächlich. Der Wert des Iterators wird sofort zurückgegeben und die Ausführung wird fortgesetzt, wenn der nächste Wert angefordert wird. Ich habe gesehen, wie Leute eine Methode wie diese mit "Yield Break" beendet haben, aber es ist unnötig. Wenn Sie das Ende der Methode erreichen, endet auch der Iterator
Loren Paulsen

Der Grund dafür ist, yield breakdass es keinen Enumerator auf Sprachebene wie a enthält. foreachWenn Sie einen Enumerator verwenden, yield breakliefert dieser einen echten Wert. Dieses Beispiel sieht aus wie eine abgewickelte Schleife. Sie werden diesen Code fast nie in der realen Welt sehen (wir können uns alle einige Randfälle vorstellen, klar), auch hier gibt es keinen "Iterator". Der "Iteratorblock" kann aus sprachlichen Gründen nicht über die Methode hinausgehen. Was tatsächlich zurückgegeben wird, ist eine "Aufzählung", siehe auch: stackoverflow.com/questions/742497/…
Shaun Wilson
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.