Einführung in Yield in C# - Was es ist, wie man es benutzt und wann es nützlich ist
Wenn man in C# zum ersten Mal auf das Schlüsselwort yield stößt, kann es verwirrend wirken. Wie genau funktioniert das? Wann sollte man yield return anstelle einer herkömmlichen return-Anweisung verwenden? Um ein umfassendes Verständnis zu erlangen, gehen wir eine detaillierte Erklärung durch, die auf dem hervorragenden YouTube-Tutorial von Tim Corey basiert: "Intro to Yield in C# - What it is, how to use it, and when it is useful."
In diesem Leitfaden verweisen wir zur einfacheren Navigation auf bestimmte Punkte in Tims Video anhand des Zeitstempels und zeigen anhand praktischer Beispiele, wie Yield den Umgang mit Datenströmen, großen Auflistungen und verzögerter Auswertung verändert.
Einführung in das Yield-Schlüsselwort in C
Tim beginnt mit einer Einführung in das yield-Schlüsselwort und weist darauf hin, dass es für Entwickler, die ihm zum ersten Mal begegnen, oft verwirrend sein kann. Er erklärt, dass die yield-Anweisung es einer Methode ermöglicht, die Ausführung anzuhalten, ihren Zustand beizubehalten und dann beim nächsten Aufruf dort fortzufahren, wo sie aufgehört hat. Tim betont, dass das Verständnis der Ausbeute entscheidend für die effiziente Verarbeitung von Daten ist, insbesondere beim Umgang mit großen Datensätzen oder bei der Implementierung benutzerdefinierter Iterationslogik.
Ein einfaches Beispiel einrichten: Klasse Program und Static Void Main
Um Ablenkungen zu vermeiden, erstellt Tim eine einfache Konsolenanwendung in Visual Studio, die er "YieldDemoApp" nennt

Was Yield in C# tatsächlich tut
Anschließend taucht Tim tiefer in die Theorie ein. Bei 2:04 beschreibt er das Verhalten von Yield: Anstatt eine ganze Sammlung auf einmal zu verarbeiten, hält die Yield-Anweisung eine Stelle fest - wie ein Daumen in einem Buch - so dass die Ausführung unterbrochen und später wieder aufgenommen werden kann.
Dieses Verhalten ist entscheidend für die verzögerte Ausführung, bei der Werte nur bei Bedarf generiert werden, anstatt alles im Voraus zu berechnen. Tims Beschreibung bildet eine klare Grundlage für das Verständnis der Funktionsweise von Yield Return.
Schreiben von Demo-Code
In der statischen void-Methode Main der Klasse Program richtet er mithilfe von Console.WriteLine grundlegende Bookend-Meldungen wie "Start of the app" und "End of the app" ein, die bei der späteren Arbeit mit einer foreach-Schleife helfen, den Ablauf klar zu visualisieren.
Dieses erste Code-Beispiel konzentriert sich ausschließlich auf die Ertragsimplementierung, ohne die Komplexität der Benutzeroberfläche zu berücksichtigen.
Erstellen der PersonModel-Klasse
Zur Veranschaulichung erstellt Tim eine PersonModel-Klasse mit den Eigenschaften FirstName und LastName und einem Konstruktor. Wenn ein PersonModel-Objekt erstellt wird, wird eine Meldung ausgegeben, die angibt, welcher Benutzer initialisiert wurde. Dies hilft bei der Veranschaulichung, wann Objekte erstellt werden und wann sie verbraucht werden.

Dieser einfache Schritt der Codegenerierung schafft die Voraussetzungen für die Arbeit mit benutzerdefinierten Iteratoren.
Aufbau der DataAccess-Klasse mit einer traditionellen Listenrückgabe
Bei 5:06 wechselt Tim zu einer DataAccess-Klasse mit einer GetPeople-Methode, die IEnumerable

Diese Iterator-Methode lädt alle Objekte sofort in den Speicher, bevor die Iteration beginnt - ein wichtiger Punkt, den Tim später der Verwendung von yield gegenüberstellt.

Demonstration des Speicherverbrauchs mit List
Nachdem die foreach-Schleife ausgeführt wurde, zeigt Tim, dass alle drei Benutzer erstellt wurden, bevor das erste Element überhaupt gelesen wurde. Hier wird ein potenzielles Problem beim Umgang mit großen Sammlungen oder großen Datensätzen deutlich - die hohe Speichernutzung.

Tim erklärt, dass wir, wenn wir beispielsweise tausend Benutzer hätten, tausend Objekte erstellen würden, auch wenn wir nur wenige benötigen, was zu einer ineffizienten Speicherzuweisung führt.
Wechseln zu Yield Return for Deferred Execution
Um 10:01 Uhr ändert Tim GetPeople, um yield return zu verwenden, anstatt eine temporäre Sammlung (eine Liste) zu erstellen. Jede Yield-Rückgabeanweisung gibt direkt ein PersonModel zu einem Zeitpunkt aus.
Dieser kritische Code innerhalb der Methode ermöglicht eine träge Auswertung, bei der das nächste Element nur dann erzeugt wird, wenn es von der foreach-Methode benötigt wird.
Tim stellt außerdem klar, dass der Rückgabetyp der Methode IEnumerable
Debugging: Wie der Compiler den Code für die Ausbeute generiert
Tim verwendet Haltepunkte, um durch den Prozess zu gehen. Er zeigt, dass die Sequenz vor dem ersten MoveNext-Aufruf leer ist. Nur wenn foreach den nächsten Wert benötigt, löst der Compiler den Iteratorblock aus und führt die Zeile yield return num aus, die das PersonModel initialisiert und zurückgibt.

Tim hebt hervor, dass der Compiler unter der Haube spezielle Zustandsmaschinen erzeugt, um die angehaltene und wieder aufgenommene Ausführung zu verwalten.
Vorteile und Effizienz der Verwendung von Yield Return
Tim erklärt, warum Rendite so effizient ist:
-
Sie können jeweils nur einen Datensatz abrufen.
-
Sie können die Anzahl der benötigten Wörter begrenzen.
-
Sie vermeiden es, die gesamte Sammlung in den Speicher zu laden.
- Sie verbessern die Skalierbarkeit, insbesondere bei der Arbeit mit großen Dateiverarbeitungen oder Datenströmen.
Anhand von LINQs .Take(2) demonstriert Tim, wie nur zwei Objekte initialisiert werden, obwohl es drei Yield-Return-Anweisungen gibt - und zeigt so die verzögerte Ausführung in Aktion.
Praktisches Beispiel: Primzahlgenerator mit Yield
Tim erstellt eine neue statische Methode IEnumerable
Dieses Beispiel zeigt, dass die Ausbeute entscheidend für den sicheren Umgang mit unendlichen Sequenzen ist.
Ohne Rendite würde der Code aufgrund des unbegrenzten Speicherverbrauchs abstürzen. Bei der Verwendung von Yield wird jedoch durch die verzögerte Ausführung sichergestellt, dass die Zahlen bei Bedarf generiert werden.
Abrufen von Primzahlen mit Take()
Anschließend zeigt Tim, wie man sicher nur eine bestimmte Anzahl von Primzahlen abrufen kann:
var primeNumbers = Generators.GetPrimeNumbers().Take(10000);
var primeNumbers = Generators.GetPrimeNumbers().Take(10000);
Dieser Code, der innerhalb der statischen Leere Main geschrieben wurde, holt effizient nur 10.000 Primzahlen ab.
Da Yield Return die Zahlen einzeln ausgibt, bleibt die Speichernutzung gering.
Benutzerdefinierte Iteration: Verwendung von GetEnumerator und MoveNext
Tim geht noch einen Schritt weiter und erklärt, wie man die Iteration manuell steuern kann.
Er erstellt einen var iterator = primeNumbers.GetEnumerator(), verwendet dann eine for-Schleife mit int i und ruft iterator.MoveNext() auf, um Elemente manuell zu holen.
Dieser manuelle Ansatz ermöglicht eine benutzerdefinierte Iteration - es wird nur bei Bedarf nach dem nächsten Wert gefragt und die Methode wird genau an der Stelle fortgesetzt, an der sie zuletzt pausiert hat.
Ein häufiger Fehler: Die Verwendung von ToList() auf Yielded Collections
Bei 36:45 warnt Tim: Die Umwandlung einer Yield-Sequenz in eine Liste mit .ToList() führt zu einer sofortigen vollständigen Auswertung.
Wenn Sie .ToList() bei einer unendlichen Sequenz aufrufen, riskieren Sie einen Absturz Ihrer Anwendung.
Tim betont, dass Yield Return für eine träge Auswertung gedacht ist und dass der Aufruf von .ToList() dieses Muster durchbricht, indem er eine vollständige Speichermaterialisierung erzwingt.
Bei der Arbeit mit LINQ-Methoden empfiehlt Tim, vorsichtig zu sein, wo Sie .ToList() einführen.
Zusammenfassung: Warum Yield ein leistungsfähiges Werkzeug ist
Abschließend betont Tim, dass das Yield-Schlüsselwort zwar einen leichten Overhead (Halten eines Zustandsautomaten) mit sich bringt, aber die Vorteile der reduzierten Speichernutzung und der verzögerten Auswertung machen es zu einem leistungsstarken Werkzeug bei der Arbeit mit:
-
Große Datensätze
-
Große Dateien
-
Benutzerdefinierte Iteratoren
-
Unendliche Sequenzen
-
Datenströme
- Aufgeschobene Bearbeitung
Er empfiehlt, mit Yield in verschiedenen Projekten zu üben, um ein tieferes Verständnis für Yield Return zu entwickeln und häufige Fallstricke zu vermeiden.
Abschließend lädt Tim die Zuschauer ein, ihm mitzuteilen, wie sie Yield im Produktionscode verwendet haben.
Abschluss
Anhand des Videos von Tim Corey haben wir gesehen, welche immensen Vorteile das Schlüsselwort yield für C# bringt. Von der Erstellung benutzerdefinierter Iteratoren bis hin zur effizienten Verwaltung großer Sammlungen - yield return ermöglicht intelligentere und speichereffizientere Funktionsrückgaben. Ganz gleich, ob Sie mit var number, var point, var reader, var connection oder großen Datensätzen arbeiten, die Beherrschung von yield kann Ihre C#-Codierkenntnisse erheblich verbessern.
Wenn Sie sich noch nicht mit der Verwendung von Yield beschäftigt haben, ist jetzt der perfekte Zeitpunkt, um es anhand einfacher Beispiele zu üben und besser zu verstehen, wie Yield Return im C#-Compiler funktioniert. Besuchen Sie auch Tims offiziellen YouTube-Kanal für weitere aufschlussreiche Videos.
