Yield return in C#: faule Magie für Anfänger

Online Python-Trainer für Anfänger

Lernen Sie Python einfach ohne theoretische Überlastung. Lösen Sie praktische Aufgaben mit automatischer Überprüfung und schreiben Sie Code direkt im Browser.

Kurs starten

Yield return in C#: Wenn Code „faul“ arbeitet und das cool ist

\n\n

Stellen Sie sich vor, Sie schreiben ein Programm, das eine Million Zeilen aus einer Logdatei verarbeiten soll. Sie laden alles brav in eine Liste List<string>, übergeben sie an eine Filtermethode – und dann, zack! – friert das Programm ein, der Speicher läuft über und der Benutzer drückt nervös Strg+Alt+Entf.

\n\n

Kommt Ihnen das bekannt vor? Wenn Sie schon einmal mit einer solchen Situation konfrontiert waren, dann ist es Zeit, die „faule Magie“ von C# kennenzulernen – den Operator yield return. Es ist nicht nur syntaktischer Zucker, sondern ein mächtiges Werkzeug, das die Art und Weise, wie Sie mit Sammlungen arbeiten, grundlegend verändern kann.

\n\n

In diesem Artikel werden wir die korrekte Verwendung von yield return anhand realer Beispiele analysieren, unter die Haube schauen und herausfinden, wann man es einsetzen sollte und wann man besser die Finger davon lässt. Los geht's!

\n\n

Was ist yield return und wie funktioniert es?

\n\n

Einfach ausgedrückt ist yield return eine Möglichkeit, dem Compiler zu sagen: „Hey, ich möchte nicht die gesamte Sammlung auf einmal zurückgeben. Lass mich die Elemente einzeln nach Bedarf ausliefern.“ Dieser Ansatz wird als faule Berechnung (lazy evaluation) bezeichnet.

\n\n

So sieht ein klassisches Beispiel aus:

\n\n
public IEnumerable<int> GetNumbers()\n{\n    yield return 1;\n    yield return 2;\n    yield return 3;\n}
\n\n

Wenn Sie diese Methode aufrufen, erhalten Sie kein Array [1, 2, 3]. Stattdessen erhalten Sie einen Iterator, der die Zahlen einzeln ausgibt, wenn Sie danach fragen. Jeder Aufruf von MoveNext() bewegt die Ausführung der Methode zum nächsten yield return.

\n\n

Das ist wie ein Fließband: Elemente werden nur dann produziert, wenn der Verbraucher sie benötigt. Kein unnötiges Kopieren, keine riesigen Listen im Speicher – nur saubere, effiziente Arbeit.

\n\n

Warum ist yield return nicht einfach eine „Rückgabe einer Liste“?

\n\n

Viele Anfänger denken: „Warum sollte ich yield return verwenden, wenn ich einfach eine List erstellen und zurückgeben kann?“. Lassen Sie uns das an einem konkreten Beispiel klären.

\n\n

Stellen Sie sich vor, Sie müssen die ersten 10 geraden Zahlen aus einer unendlichen Sequenz abrufen. Mit yield return geht das elegant:

\n\n
public IEnumerable<int> GetEvenNumbers()\n{\n    int i = 0;\n    while (true)\n    {\n        yield return i;\n        i += 2;\n    }\n}\n\n// Verwendung\nvar firstTenEven = GetEvenNumbers().Take(10).ToList();
\n\n

Versuchen Sie dasselbe mit List<int> – entweder landen Sie in einer Endlosschleife oder Sie müssen alle generierten Zahlen im Speicher behalten. Yield return gibt uns die Möglichkeit, mit potenziell unendlichen Sequenzen zu arbeiten, ohne den Arbeitsspeicher zu überlasten.

\n\n

Praktisches Beispiel: Lesen einer großen Datei

\n\n

Kommen wir zurück zu unserer Logdatei mit einer Million Zeilen. So schreiben Anfänger normalerweise:

\n\n
public List<string> ReadLogLines(string path)\n{\n    var lines = new List<string>();\n    using (var reader = new StreamReader(path))\n    {\n        string line;\n        while ((line = reader.ReadLine()) != null)\n        {\n            lines.Add(line);\n        }\n    }\n    return lines;\n}
\n\n

Das Problem: Die gesamte Datei wird in den Speicher geladen. Wenn die Datei 500 MB wiegt, wird die Liste ungefähr genauso viel wiegen. Und wenn Sie nur die Zeilen mit Fehlern filtern müssen? Sie laden trotzdem alles.

\n\n

Hier ist die korrekte Verwendung von yield return:

\n\n
public IEnumerable<string> ReadLogLines(string path)\n{\n    using (var reader = new StreamReader(path))\n    {\n        string line;\n        while ((line = reader.ReadLine()) != null)\n        {\n            yield return line;\n        }\n    }\n}\n\n// Filtern ohne die gesamte Datei zu laden\nvar errorLines = ReadLogLines("log.txt")\n    .Where(line => line.Contains("ERROR"))\n    .Take(100);<                

Blogs

Buchempfehlungen