Das Yield- Schlüsselwort ist eines dieser Schlüsselwörter in C #, das mich immer wieder mystifiziert, und ich war mir nie sicher, ob ich es richtig verwende.
Welcher der beiden folgenden Codeteile ist der bevorzugte und warum?
Version 1: Ertragsrendite verwenden
public static IEnumerable<Product> GetAllProducts()
{
using (AdventureWorksEntities db = new AdventureWorksEntities())
{
var products = from product in db.Product
select product;
foreach (Product product in products)
{
yield return product;
}
}
}
Version 2: Geben Sie die Liste zurück
public static IEnumerable<Product> GetAllProducts()
{
using (AdventureWorksEntities db = new AdventureWorksEntities())
{
var products = from product in db.Product
select product;
return products.ToList<Product>();
}
}
c#
yield-return
Senfo
quelle
quelle
yield
ist gebundenIEnumerable<T>
und seine Art. Es ist in einer irgendwie faulen Bewertungyield return
wenn der Code, der die Ergebnisse von durchläuft,GetAllProducts()
dem Benutzer die Möglichkeit gibt, die Verarbeitung vorzeitig abzubrechen.Antworten:
Ich neige dazu, die Rendite zu verwenden, wenn ich den nächsten Artikel in der Liste (oder sogar die nächste Gruppe von Artikeln) berechne.
Bei Verwendung Ihrer Version 2 müssen Sie über die vollständige Liste verfügen, bevor Sie zurückkehren können. Wenn Sie Yield-Return verwenden, müssen Sie wirklich nur den nächsten Artikel haben, bevor Sie zurückkehren.
Dies hilft unter anderem dabei, die Berechnungskosten komplexer Berechnungen über einen größeren Zeitraum zu verteilen. Wenn die Liste beispielsweise mit einer GUI verbunden ist und der Benutzer niemals zur letzten Seite wechselt, berechnen Sie niemals die endgültigen Elemente in der Liste.
Ein anderer Fall, in dem Rendite-Rendite vorzuziehen ist, ist, wenn IEnumerable eine unendliche Menge darstellt. Betrachten Sie die Liste der Primzahlen oder eine unendliche Liste von Zufallszahlen. Sie können niemals die vollständige IEnumerable auf einmal zurückgeben, daher verwenden Sie Yield-Return, um die Liste schrittweise zurückzugeben.
In Ihrem speziellen Beispiel haben Sie die vollständige Liste der Produkte, daher würde ich Version 2 verwenden.
quelle
Yield return
scheint eine Abkürzung für das Schreiben einer eigenen benutzerdefinierten Iterator-Klasse zu sein (IEnumerator implementieren). Daher gelten die genannten Vorteile auch für benutzerdefinierte Iteratorklassen. Auf jeden Fall behalten beide Konstrukte den Zwischenzustand bei. In seiner einfachsten Form geht es darum, einen Verweis auf das aktuelle Objekt zu halten.Das Auffüllen einer temporären Liste ist wie das Herunterladen des gesamten Videos, während das Verwenden
yield
wie das Streamen dieses Videos ist.quelle
Nehmen
yield
wir als konzeptionelles Beispiel für das Verständnis, wann Sie verwenden sollten, an , dass die MethodeConsumeLoop()
die zurückgegebenen / zurückgegebenen Elemente verarbeitet vonProduceList()
:Ohne
yield
kann der Anruf beiProduceList()
lange dauern, da Sie die Liste vervollständigen müssen, bevor Sie zurückkehren:Mit
yield
wird es neu angeordnet und arbeitet "parallel":Und schließlich sollten Sie, wie viele bereits vorgeschlagen haben, Version 2 verwenden, da Sie die fertige Liste ohnehin schon haben.
quelle
Ich weiß, dass dies eine alte Frage ist, aber ich möchte ein Beispiel dafür anbieten, wie das Yield-Schlüsselwort kreativ verwendet werden kann. Ich habe wirklich von dieser Technik profitiert. Hoffentlich hilft dies allen anderen, die über diese Frage stolpern.
Hinweis: Stellen Sie sich das Yield-Schlüsselwort nicht nur als eine andere Möglichkeit zum Erstellen einer Sammlung vor. Ein großer Teil der Ertragskraft besteht darin, dass die Ausführung in Ihrer Methode oder Eigenschaft angehalten wird, bis der aufrufende Code über den nächsten Wert iteriert. Hier ist mein Beispiel:
Die Verwendung des Yield-Schlüsselworts (neben Rob Eisenburgs Implementierung von Caliburn.Micro Coroutines ) ermöglicht es mir, einen asynchronen Aufruf an einen Webdienst wie diesen auszudrücken:
Dadurch wird mein BusyIndicator aktiviert, die Anmeldemethode in meinem Webdienst aufgerufen, mein IsLoggedIn-Flag auf den Rückgabewert gesetzt und dann der BusyIndicator wieder deaktiviert.
So funktioniert das: IResult verfügt über eine Execute-Methode und ein Completed-Ereignis. Caliburn.Micro greift den IEnumerator aus dem Aufruf von HandleButtonClick () ab und übergibt ihn an eine Coroutine.BeginExecute-Methode. Die BeginExecute-Methode beginnt mit der Iteration durch die IResults. Wenn das erste IResult zurückgegeben wird, wird die Ausführung in HandleButtonClick () angehalten, und BeginExecute () fügt dem Ereignis Completed einen Ereignishandler hinzu und ruft Execute () auf. IResult.Execute () kann entweder eine synchrone oder eine asynchrone Aufgabe ausführen und löst das Ereignis "Abgeschlossen" aus, wenn es abgeschlossen ist.
LoginResult sieht ungefähr so aus:
Es kann hilfreich sein, so etwas einzurichten und die Ausführung zu durchlaufen, um zu beobachten, was los ist.
Hoffe das hilft jemandem! Ich habe es wirklich genossen, die verschiedenen Verwendungsmöglichkeiten von Erträgen zu erkunden.
quelle
yield
auf diese Weise zu verwenden. Es scheint eine elegante Möglichkeit zu sein, das Async / Wait-Muster zu emulieren (von dem ich annehme, dass es verwendet wird, anstattyield
wenn es heute neu geschrieben würde). Haben Sie festgestellt, dass diese kreativen Verwendungen vonyield
(kein Wortspiel beabsichtigt) im Laufe der Jahre zu sinkenden Renditen geführt haben, da sich C # seit der Beantwortung dieser Frage weiterentwickelt hat? Oder haben Sie immer noch modernisierte clevere Anwendungsfälle wie diesen? Und wenn ja, würde es Ihnen etwas ausmachen, ein weiteres interessantes Szenario für uns zu teilen?Dies wird wie ein bizarrer Vorschlag erscheinen, aber ich habe gelernt, wie man das
yield
Schlüsselwort in C # verwendet, indem ich eine Präsentation über Generatoren in Python gelesen habe: David M. Beazleys http://www.dabeaz.com/generators/Generators.pdf . Sie müssen nicht viel Python wissen, um die Präsentation zu verstehen - ich habe es nicht getan. Ich fand es sehr hilfreich, nicht nur zu erklären, wie Generatoren funktionieren, sondern warum Sie sich darum kümmern sollten.quelle
Die Rendite kann für Algorithmen sehr leistungsfähig sein, bei denen Sie Millionen von Objekten durchlaufen müssen. Betrachten Sie das folgende Beispiel, in dem Sie mögliche Fahrten für die Mitfahrgelegenheit berechnen müssen. Zuerst generieren wir mögliche Reisen:
Dann durchlaufen Sie jede Reise:
Wenn Sie List anstelle von Yield verwenden, müssen Sie 1 Million Objekte dem Speicher zuweisen (~ 190 MB). Die Ausführung dieses einfachen Beispiels dauert ca. 1400 ms. Wenn Sie jedoch Yield verwenden, müssen Sie nicht alle diese temporären Objekte im Speicher ablegen, und Sie erhalten eine erheblich schnellere Algorithmusgeschwindigkeit: In diesem Beispiel dauert die Ausführung ohne Speicherverbrauch nur ca. 400 ms.
quelle
yield
arbeitet unter dem Deckmantel, indem eine Zustandsmaschine intern implementiert wird. Hier ist eine SO-Antwort mit 3 detaillierten MSDN-Blog-Posts , die die Implementierung ausführlich erläutern. Geschrieben von Raymond Chen @ MSFTDie beiden Codeteile machen wirklich zwei verschiedene Dinge. Die erste Version zieht Mitglieder nach Bedarf. Die zweite Version lädt alle Ergebnisse in den Speicher, bevor Sie damit beginnen.
Es gibt keine richtige oder falsche Antwort auf diese Frage. Welches vorzuziehen ist, hängt nur von der Situation ab. Wenn Sie beispielsweise nur eine begrenzte Zeit zum Abschließen Ihrer Abfrage benötigen und mit den Ergebnissen etwas halbkompliziertes tun müssen, ist die zweite Version möglicherweise vorzuziehen. Achten Sie jedoch auf große Ergebnismengen, insbesondere wenn Sie diesen Code im 32-Bit-Modus ausführen. Ich bin bei dieser Methode mehrmals von OutOfMemory-Ausnahmen gebissen worden.
Das Wichtigste ist jedoch Folgendes: Die Unterschiede liegen in der Effizienz. Daher sollten Sie sich wahrscheinlich für das entscheiden, was Ihren Code einfacher macht, und ihn erst nach der Profilerstellung ändern.
quelle
Der Ertrag hat zwei großartige Verwendungsmöglichkeiten
Es ist hilfreich, eine benutzerdefinierte Iteration bereitzustellen, ohne temporäre Sammlungen zu erstellen. (Laden aller Daten und Schleifen)
Es hilft, eine zustandsbehaftete Iteration durchzuführen. (Streaming)
Unten ist ein einfaches Video, das ich mit vollständiger Demonstration erstellt habe, um die beiden oben genannten Punkte zu unterstützen
http://www.youtube.com/watch?v=4fju3xcm21M
quelle
Dies ist, was Chris Sells über diese Aussagen in der C # -Programmiersprache erzählt ;
quelle
F().Any()
dies zurückgegeben, nachdem versucht wurde, nur das erste Ergebnis aufzuzählen. Im Allgemeinen sollten Sie sich nicht darauf verlassen, dass einIEnumerable yield
Programmstatus geändert wird, da er möglicherweise nicht ausgelöst wirdAngenommen, die LINQ-Klasse Ihrer Produkte verwendet eine ähnliche Ausbeute für die Aufzählung / Iteration. Die erste Version ist effizienter, da sie bei jeder Iteration nur einen Wert liefert.
Das zweite Beispiel ist das Konvertieren des Enumerators / Iterators in eine Liste mit der ToList () -Methode. Dies bedeutet, dass alle Elemente im Enumerator manuell durchlaufen werden und dann eine flache Liste zurückgegeben wird.
quelle
Dies ist ein bisschen nebensächlich, aber da die Frage als Best Practices gekennzeichnet ist, werde ich meine zwei Cent einwerfen. Für diese Art von Dingen ziehe ich es sehr vor, daraus eine Immobilie zu machen:
Sicher, es ist etwas mehr Kesselplatte, aber der Code, der dies verwendet, sieht viel sauberer aus:
vs.
Hinweis: Ich würde dies nicht für Methoden tun, deren Ausführung eine Weile dauern kann.
quelle
Und was ist damit?
Ich denke, das ist viel sauberer. Ich habe jedoch kein VS2008 zur Hand, um es zu überprüfen. Wenn Products IEnumerable implementiert (wie es scheint - es wird in einer foreach-Anweisung verwendet), würde ich es auf jeden Fall direkt zurückgeben.
quelle
In diesem Fall hätte ich Version 2 des Codes verwendet. Da Sie über die vollständige Liste der verfügbaren Produkte verfügen und dies vom "Verbraucher" dieses Methodenaufrufs erwartet wird, müssen die vollständigen Informationen an den Anrufer zurückgesendet werden.
Wenn der Aufrufer dieser Methode jeweils "eine" Information benötigt und der Verbrauch der nächsten Information auf Abruf erfolgt, ist es vorteilhaft, die Ertragsrückgabe zu verwenden, um sicherzustellen, dass der Ausführungsbefehl an den Aufrufer zurückgegeben wird, wenn Eine Informationseinheit ist verfügbar.
Einige Beispiele, bei denen man die Rendite verwenden könnte, sind:
Um Ihre Fragen zu beantworten, hätte ich die Version 2 verwendet.
quelle
Geben Sie die Liste direkt zurück. Leistungen:
Die Liste ist wiederverwendbar. (der Iterator ist nicht)nicht wirklich wahr, danke JonSie sollten den Iterator (Yield) verwenden, wenn Sie der Meinung sind, dass Sie wahrscheinlich nicht bis zum Ende der Liste iterieren müssen oder wenn er kein Ende hat. Zum Beispiel wird der Client-Aufruf nach dem ersten Produkt suchen, das ein bestimmtes Prädikat erfüllt. Sie können den Iterator verwenden, obwohl dies ein erfundenes Beispiel ist und es wahrscheinlich bessere Möglichkeiten gibt, dies zu erreichen. Wenn Sie im Voraus wissen, dass die gesamte Liste berechnet werden muss, tun Sie dies einfach im Voraus. Wenn Sie der Meinung sind, dass dies nicht der Fall ist, sollten Sie die Iteratorversion verwenden.
quelle
Die Schlüsselrendite für die Ertragsrückgabe wird verwendet, um die Zustandsmaschine für eine bestimmte Sammlung zu verwalten. Überall dort, wo die CLR die verwendete Schlüsselrendite für die Ertragsrückgabe sieht, implementiert die CLR ein Enumerator-Muster für diesen Code. Diese Art der Implementierung hilft dem Entwickler bei allen Arten von Installationen, die wir ohne das Schlüsselwort sonst hätten ausführen müssen.
Angenommen, der Entwickler filtert eine Sammlung, durchläuft die Sammlung und extrahiert diese Objekte in einer neuen Sammlung. Diese Art der Installation ist ziemlich eintönig.
Mehr zum Schlüsselwort hier in diesem Artikel .
quelle
Die Nutzung der Ausbeute ist vergleichbar mit dem Schlüsselwort return , außer dass es einen zurück Generator . Und das Generatorobjekt wird nur einmal durchlaufen .
Ertrag hat zwei Vorteile:
Es gibt eine andere klare Erklärung , die Ihnen vielleicht helfen könnte.
quelle