2008-10-23 7 views
380

Ich habe diese Syntax in MSDN: yield break gesehen, aber ich weiß nicht, was es tut. Weiß jemand?Was bedeutet "Ausbeute brechen" in C# machen?

+16

Die MSDN-Dokumentation erwähnt es jedoch, aber es erklärt es nicht. Das ist ein schlechter Weg, einen wissenshungrigen Entwickler zu ärgern. – Phil

+2

Ertragsrückgabe eliminiert die Notwendigkeit für eine Backing-Liste, das heißt, Sie müssen nicht so etwas wie 'MyList.Add (...)' nur tun 'return return ...'. Wenn Sie vorzeitig aus der Schleife ausbrechen und die virtuelle Hintergrundliste zurückgeben müssen, verwenden Sie 'yield break;' –

+0

Solide Empfehlung von "The Muffin Man" –

Antwort

418

Es gibt an, dass ein Iterator zu einem Ende gekommen ist. Sie können sich yield break als eine return-Anweisung vorstellen, die keinen Wert zurückgibt.

Zum Beispiel, wenn Sie eine Funktion als Iterator definieren, der Körper der Funktion kann wie folgt aussehen:

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

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

Beachten Sie, dass, nachdem die Schleife alle seine Zyklen abgeschlossen ist, wird die letzte Zeile ausgeführt und Sie werden die Nachricht in Ihrer Konsolen-App sehen.

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 ist die letzte Aussage nie, weil wir die Funktion vorzeitig verlassen ausgeführt wird.

+3

Könnte es einfach 'break' anstelle von' yield break' in Ihrem obigen Beispiel sein? Der Compiler beklagt sich nicht darüber. – orad

+48

@orad ein einfacher 'break' in diesem Fall würde die Schleife stoppen, aber es würde die Methodenausführung nicht abbrechen, somit würde die letzte Zeile ausgeführt werden und der" Will not me text "würde tatsächlich gesehen werden. –

+0

@ DamirZekić, beendet der Iterator alle Zyklen im Falle einer Pause? – machinarium

45

Beendet einen Iteratorblock (z. B. gibt es keine weiteren Elemente in IEnumerable).

+1

mit [+1] - obwohl akademisch gibt es keine Iteratoren in .NET . nur Enumeratoren (one direction, forward.) –

+0

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

+0

@monstr Sie Sammlung in jedem iterieren können Mode und du brauchst keine "Ausbeute", um es zu tun. Ich sage nicht, dass du falsch liegst, ich sehe, was du vorschlägst; Aber akademisch gibt es keine Iteratoren in .NET, nur Enumeratoren (eine Richtung, vorwärts) - im Gegensatz zu stdC++ gibt es kein "generisches Iterator-Framework", das in CTS/CLR definiert ist.LINQ hilft, die Lücke mit Erweiterungsmethoden zu schließen, die 'yield return' ** und auch Callback-Methoden ** verwenden, aber sie sind erstklassige Erweiterungsmethoden, nicht erstklassige Iteratoren. Das resultierende 'IEnumerable' kann nicht selbst in eine andere Richtung als die Vorwärtsrichtung des Aufrufers iterieren. –

4

Wenn, was Sie mit „was Pause tut Ausbeute wirklich tun“, wird „wie funktioniert es“ - Siehe Raymond Chen Blog für Details http://blogs.msdn.com/oldnewthing/archive/2008/08/12/8849519.aspx

C# Iteratoren einig sehr komplizierten Code generieren.

+10

Nun, sie sind nur kompliziert, wenn Sie sich für den Code interessieren, in den sie kompiliert werden. Code, der sie verwendet, ist ziemlich einfach. –

+0

@Robert, das ist, was ich meinte, also habe ich die Antwort aktualisiert, um Ihren Kommentar zu enthalten –

27

Weist den Iterator an, dass er das Ende erreicht hat.

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; 
    } 
} 
10

Das ganze Thema der Iterator-Blöcke ist in diesem free sample chapter aus Jon Skeets Buch C# in Depth gut abgedeckt.

+0

Sie haben mir gerade eine Kopie verkauft. Vielen Dank! :-) –

18

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

yield return ist wie ein Thread, der eine "Schedule" oder "Schlaf" -Funktion aufruft, um die Kontrolle über die CPU aufzugeben. Genau wie ein Thread, die IEnumerable<T> Methode gewinnt Kontrollen an dem Punkt unmittelbar danach, mit allen lokalen Variablen, die die gleichen Werte haben, die sie hatten, bevor Kontrolle aufgegeben wurde.

yield break ist wie ein Thread, der das Ende seiner Funktion erreicht und beendet.

Die Leute sprechen über eine "State Machine", aber eine State Machine ist alles ein "Thread" ist wirklich. Ein Thread hat einen Zustand (z. B. Werte von lokalen Variablen), und jedes Mal, wenn er geplant wird, benötigt er einige Maßnahmen, um einen neuen Zustand zu erreichen. Der entscheidende Punkt über yield ist, dass im Gegensatz zu den Betriebssystem-Threads, die wir gewohnt sind, der Code, der es verwendet, in der Zeit eingefroren wird, bis die Iteration manuell erweitert oder beendet wird.

6

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

 
public static IEnumerable<int> Range(int min, int max) 
{ 
    while (true) 
    { 
     if (min >= max) 
     { 
     yield break; 
     } 
     yield return min++; 
    } 
} 

und Erklärung, dass, wenn eine yield break Anweisung innerhalb eines Verfahrens getroffen wird, wird die Ausführung dieser Methode ohne Rückkehr stoppt. Es gibt einige Zeitsituationen, in denen Sie kein Ergebnis geben möchten, dann können Sie die Ertragspause verwenden.

0

Das Schlüsselwort yield wird zusammen mit dem Schlüsselwort return verwendet, um dem Enumeratorobjekt einen Wert zuzuweisen. yield return gibt den Wert oder die Werte an, die zurückgegeben werden. Wenn die Yield Return-Anweisung erreicht ist, wird der aktuelle Standort gespeichert. Die Ausführung wird von diesem Ort aus neu gestartet, wenn der Iterator das nächste Mal aufgerufen wird.

Um die Bedeutung anhand eines Beispiels zu erklären:

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

     counter = counter + 2; 

     yield return counter; 

     counter = counter + 3; 

     yield return counter ; 
    } 

Werte zurückgegeben, wenn diese iterativen ist sind: 0, 2, 5.

Es ist wichtig, dass Zähler beachten Variable in diesem Beispiel ist eine lokale Variable. Nach der zweiten Iteration, die den Wert von 2, die dritte Iteration zurück beginnt, von wo sie vor gelassen, während der vorherige Wert der lokalen Variablen Zähler genannt Erhaltung der 2.

+10

Sie haben nicht erklärt, was "Ausbeute brechen" tut –

+0

Ich glaube nicht, dass "Rendite" tatsächlich die Rückgabe mehrerer Werte unterstützt. Vielleicht hast du das nicht gemeint, aber so habe ich es gelesen. – Sam

+0

Sam - die SampleNumbers-Methode mit mehreren Rendite-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 Leute gesehen, die eine solche Methode mit "yield pause" beenden, aber das ist unnötig. Das Ende der Methode zu schlagen beendet auch den Iterator –

4

Die yield break Anweisung bewirkt, dass die Aufzählung zu stoppen war. In der Tat vervollständigt yield break die Aufzählung, ohne irgendwelche zusätzlichen Elemente zurückzugeben.

Beachten Sie, dass es zwei Möglichkeiten gibt, wie eine Iterator-Methode 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 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 wird die Iteratormethode natürlich stoppt, sobald maxCount Primzahlen Ausführung gefunden wurden. Die yield break-Anweisung ist eine weitere Möglichkeit für den Iterator, die Aufzählung aufzugeben. Es ist ein Weg, um früh aus der Aufzählung auszubrechen. Hier ist die gleiche Methode wie oben. Dieses Mal hat die Methode ein Limit für die Zeit, die die Methode ausgeführt werden 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. In der Tat wird die Aufzählung früh beendet.

Beachten Sie auch, dass die yield break anders funktioniert als nur eine einfache break. Im obigen Beispiel verlässt yield break das Verfahren, ohne den Aufruf an Debug.WriteLine(..) zu tätigen.