Nuta
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Instrukcja jest używana yield w iteratorze, aby podać następną wartość lub zasygnalizować koniec iteracji. Instrukcja yield ma dwie następujące formy:
yield return: aby podać następną wartość w iteracji, jak pokazano w poniższym przykładzie: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: aby jawnie zasygnalizować koniec iteracji, jak pokazano w poniższym przykładzie: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; } } }Iteracja kończy się również po osiągnięciu końca iteratora kontrolki.
W poprzednich przykładach zwracany typ iteratorów to IEnumerable<T> (w niegenerycznych przypadkach jako IEnumerable typ zwracany iteratora). Można również użyć IAsyncEnumerable<T> jako zwracanego typu iteratora. To sprawia, że iterator jest asynchroniczny. Użyj instrukcji await foreach , aby iterować wynik iteratora, jak pokazano w poniższym przykładzie:
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;
}
IEnumerator<T> lub IEnumerator może być również zwracanym typem iteratora. Użyj tych typów zwracanych podczas implementowania GetEnumerator metody w następujących scenariuszach:
Projektujesz typ implementujący IEnumerable<T> lub IEnumerable interfejs.
Możesz dodać wystąpienie lub metodę rozszerzenia
GetEnumerator, aby włączyć iterację w wystąpieniuforeachtypu za pomocą instrukcji , jak pokazano w poniższym przykładzie: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; } }
Nie można używać instrukcji yield w:
- metody z w, reflub z w parametrach.
- wyrażenia lambda i anonimowe metody.
-
niebezpieczne bloki. Przed językiem C# 13
yieldbył nieprawidłowy w dowolnej metodzie z blokiemunsafe. Począwszy od języka C# 13, można użyćyieldmetod z blokamiunsafe, ale nie wunsafebloku. -
yield returniyield breaknie można ich używać w blokach catch i finally lub w blokach try z odpowiednimcatchblokiem. Instrukcjeyield returniyield breakmożna używać wtrybloku bezcatchbloków, tylkofinallybloku.
using instrukcje w iteratorach
Instrukcje można używać using w metodach iteratora. Ponieważ using instrukcje są kompilowane w try blokach z klauzulami finally (i bez catch bloków), działają prawidłowo z iteratorami. Zasoby jednorazowe są prawidłowo zarządzane w trakcie wykonywania iteratora:
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.");
}
Jak pokazano w poprzednim przykładzie, zasób uzyskany w using instrukcji pozostaje dostępny w trakcie wykonywania iteratora, nawet gdy iterator zawiesza się i wznawia wykonywanie instrukcji yield return . Zasób jest usuwany po zakończeniu iteratora (przez dotarcie do końca lub za pośrednictwem yield break) lub gdy sam iterator zostanie usunięty (na przykład gdy obiekt wywołujący wypadnie z wyliczenia wcześnie).
Wykonywanie iteratora
Wywołanie iteratora nie wykonuje go natychmiast, jak pokazano w poniższym przykładzie:
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.
Jak pokazano w poprzednim przykładzie, gdy zaczniesz iterować wynik iteratora, iterator jest wykonywany do momentu osiągnięcia pierwszej yield return instrukcji. Następnie wykonanie iteratora jest zawieszone, a obiekt wywołujący pobiera pierwszą wartość iteracji i przetwarza ją. W każdej kolejnej iteracji wykonanie iteratora zostanie wznowione po yield return instrukcji, która spowodowała poprzednie zawieszenie i będzie kontynuowana do momentu osiągnięcia kolejnej yield return instrukcji. Iteracji kończy się, gdy kontrolka osiągnie koniec iteratora lub instrukcji yield break .
specyfikacja języka C#
Aby uzyskać więcej informacji, zobacz sekcję instrukcji yield specyfikacji języka C#.