Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Verwenden Sie die yield Anweisung in einem Iterator , um den nächsten Wert anzugeben oder das Ende einer Iteration zu signalisieren. Die yield-Anweisung weist die folgenden zwei Formen auf:
yield return, um den nächsten Wert in der Iteration bereitzustellen, wie das folgende Beispiel zeigt:foreach (int i in ProduceEvenNumbers(9)) { Console.Write(i); Console.Write(" "); } // Output: 0 2 4 6 8 IEnumerable<int> ProduceEvenNumbers(int upto) { for (int i = 0; i <= upto; i += 2) { yield return i; } }yield break, um das Ende der Iteration explizit zu signalisieren, wie das folgende Beispiel zeigt:Console.WriteLine(string.Join(" ", TakeWhilePositive(new int[] {2, 3, 4, 5, -1, 3, 4}))); // Output: 2 3 4 5 Console.WriteLine(string.Join(" ", TakeWhilePositive(new int[] {9, 8, 7}))); // Output: 9 8 7 IEnumerable<int> TakeWhilePositive(IEnumerable<int> numbers) { foreach (int n in numbers) { if (n > 0) { yield return n; } else { yield break; } } }Die Iteration wird auch abgeschlossen, wenn das Steuerelement das Ende eines Iterators erreicht.
Die C#-Sprachreferenz dokumentiert die zuletzt veröffentlichte Version der C#-Sprache. Außerdem enthält sie erste Dokumentation für Features in der öffentlichen Vorschau für die kommende Sprachversion.
In der Dokumentation werden alle Features identifiziert, die in den letzten drei Versionen der Sprache oder in der aktuellen öffentlichen Vorschau eingeführt wurden.
Tipp
Informationen dazu, wann ein Feature erstmals in C# eingeführt wurde, finden Sie im Artikel zum Versionsverlauf der C#-Sprache.
In den vorherigen Beispielen lautet IEnumerable<T>der Rückgabetyp der Iteratoren . Verwenden Sie IEnumerable in nichtgenerischen Fällen als Rückgabetyp eines Iterators. Sie können auch IAsyncEnumerable<T> als Rückgabetyp eines Iterators verwenden. Dadurch wird ein Iterator asynchron. Verwenden Sie die await foreach-Anweisung, um das Ergebnis des Iterators zu durchlaufen, wie das folgende Beispiel zeigt:
await foreach (int n in GenerateNumbersAsync(5))
{
Console.Write(n);
Console.Write(" ");
}
// Output: 0 2 4 6 8
async IAsyncEnumerable<int> GenerateNumbersAsync(int count)
{
for (int i = 0; i < count; i++)
{
yield return await ProduceNumberAsync(i);
}
}
async Task<int> ProduceNumberAsync(int seed)
{
await Task.Delay(1000);
return 2 * seed;
}
Der Rückgabetyp eines Iterators kann auch IEnumerator<T> oder IEnumerator sein. Verwenden Sie diese Rückgabetypen, wenn Sie die GetEnumerator-Methode in den folgenden Szenarien implementieren:
Sie entwerfen den Typ, der die IEnumerable<T>- oder IEnumerable-Schnittstelle implementiert.
Sie fügen die Instanz- oder Erweiterungs-Methode
GetEnumeratorhinzu, um die Iteration über die Instanz des Typs mit derforeach-Anweisung zu aktivieren, wie das folgende Beispiel zeigt:public static void Example() { var point = new Point(1, 2, 3); foreach (int coordinate in point) { Console.Write(coordinate); Console.Write(" "); } // Output: 1 2 3 } public readonly record struct Point(int X, int Y, int Z) { public IEnumerator<int> GetEnumerator() { yield return X; yield return Y; yield return Z; } }
Sie können die yield-Anweisungen hierin nicht verwenden:
- Methoden mit in-, ref- oder out-Parametern.
- Lambda-Ausdrücke und anonyme Methoden.
-
unsichere Blöcke. Vor C# 13 war
yieldin jeder Methode mit einemunsafe-Block ungültig. Ab C# 13 können Sieyieldin Methoden mitunsafe-Blöcken, aber nicht imunsafe-Block verwenden. -
yield returnundyield breakkann nicht in Catch - und schließlich-Blöcken oder in Try-Blocks mit einem entsprechendencatchBlock verwendet werden. Dieyield returnAnweisungen undyield breakAnweisungen können in einemtryBlock ohnecatchBlöcke verwendet werden, nur einenfinallyBlock.
using Anweisungen in Iteratoren
Sie können Anweisungen in Iteratormethoden verwendenusing. Da using Anweisungen in try Blöcke mit finally Klauseln (und ohne catch Blöcke) kompiliert werden, funktionieren sie ordnungsgemäß mit Iteratoren. Die verfügbaren Ressourcen werden während der gesamten Ausführung des Iterators ordnungsgemäß verwaltet:
Console.WriteLine("=== Using in Iterator Example ===");
// Demonstrate that using statements work correctly in iterators
foreach (string line in ReadLinesFromResource())
{
Console.WriteLine($"Read: {line}");
// Simulate processing only first two items
if (line == "Line 2") break;
}
Console.WriteLine("Iteration stopped early - resource should still be disposed.");
static IEnumerable<string> ReadLinesFromResource()
{
Console.WriteLine("Opening resource...");
using var resource = new StringWriter(); // Use StringWriter as a simple IDisposable
resource.WriteLine("Resource initialized");
// These lines would typically come from the resource (e.g., file, database)
string[] lines = { "Line 1", "Line 2", "Line 3", "Line 4" };
foreach (string line in lines)
{
Console.WriteLine($"About to yield: {line}");
yield return line;
Console.WriteLine($"Resumed after yielding: {line}");
}
Console.WriteLine("Iterator completed - using block will dispose resource.");
}
Wie im vorherigen Beispiel gezeigt, bleibt die in der using Anweisung erworbene Ressource während der gesamten Ausführung des Iterators verfügbar, auch wenn der Iterator die Ausführung an yield return Anweisungen anhält und fortsetzt. Die Ressource wird verworfen, wenn der Iterator abgeschlossen ist (entweder durch Erreichen des Endes oder über yield break) oder wenn der Iterator selbst verworfen wird (z. B. wenn der Aufrufer frühzeitig aus der Enumeration ausbricht).
Ausführung eines Iterators
Wenn Sie einen Iterator aufrufen, wird er nicht sofort ausgeführt, wie im folgenden Beispiel gezeigt:
var numbers = ProduceEvenNumbers(5);
Console.WriteLine("Caller: about to iterate.");
foreach (int i in numbers)
{
Console.WriteLine($"Caller: {i}");
}
IEnumerable<int> ProduceEvenNumbers(int upto)
{
Console.WriteLine("Iterator: start.");
for (int i = 0; i <= upto; i += 2)
{
Console.WriteLine($"Iterator: about to yield {i}");
yield return i;
Console.WriteLine($"Iterator: yielded {i}");
}
Console.WriteLine("Iterator: end.");
}
// Output:
// Caller: about to iterate.
// Iterator: start.
// Iterator: about to yield 0
// Caller: 0
// Iterator: yielded 0
// Iterator: about to yield 2
// Caller: 2
// Iterator: yielded 2
// Iterator: about to yield 4
// Caller: 4
// Iterator: yielded 4
// Iterator: end.
Wie im vorherigen Beispiel gezeigt, wird der Iterator ausgeführt, bis die erste yield return Anweisung erreicht ist, wenn Sie mit dem Durchlaufen des Ergebnisses eines Iterators beginnen. Anschließend wird die Ausführung des Iterators angehalten, und der Aufrufer ruft den ersten Iterationswert ab und verarbeitet ihn. Bei jeder nachfolgenden Iteration wird die Ausführung des Iterators nach der yield return Anweisung fortgesetzt, die die vorherige Unterbrechung verursacht hat, und wird fortgesetzt, bis die nächste yield return Anweisung erreicht ist. Die Iteration wird abgeschlossen, wenn das Steuerelement das Ende eines Iterators oder einer yield break-Anweisung erreicht.
C#-Sprachspezifikation
Weitere Informationen finden Sie im Abschnitt Die yield-Anweisung der C#-Sprachspezifikation.