Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Usare l'istruzione yield in un iteratore per fornire il valore successivo o segnalare la fine di un'iterazione. L'istruzione yield presenta le due forme seguenti:
yield return: per fornire il valore successivo nell'iterazione, come illustrato nell'esempio seguente: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: per segnalare in modo esplicito la fine dell'iterazione, come illustrato nell'esempio seguente: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; } } }L'iterazione termina anche quando il controllo raggiunge la fine di un iteratore.
Negli esempi precedenti il tipo restituito di iteratori è IEnumerable<T> (in casi non generici, usare IEnumerable come tipo restituito di un iteratore). È anche possibile usare IAsyncEnumerable<T> come tipo restituito di un iteratore. Questo rende un iteratore asincrono. Usare l'istruzione await foreach per iterare il risultato dell'iteratore, come illustrato nell'esempio seguente:
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> o IEnumerator può anche essere il tipo restituito di un iteratore. Usare questi tipi restituiti quando si implementa il metodo GetEnumerator negli scenari seguenti:
Si progetta il tipo che implementa l’interfaccia IEnumerable<T> o IEnumerable.
Si aggiunge un'istanza o un metodo di estensione
GetEnumeratorper abilitare l'iterazione sull'istanza del tipo con l'istruzioneforeach, come illustrato nell'esempio seguente: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; } }
Non è possibile usare le istruzioni yield in:
- metodi con parametri in, ref, o out.
- espressioni lambda e metodi anonimi.
-
blocchi non sicuri. Prima di C# 13,
yieldnon era valido in nessun metodo con un bloccounsafe. A partire da C# 13, è possibile usareyieldnei metodi con blocchiunsafe, ma non nel bloccounsafe. -
yield returneyield breaknon possono essere usati nei blocchi catch e finally oppure in blocchi try con un blocco corrispondentecatch. Leyield returnistruzioni eyield breakpossono essere usate in untryblocco senzacatchblocchi, ma solo in unfinallyblocco.
using istruzioni negli iteratori
È possibile usare using istruzioni nei metodi iteratore. Poiché using le istruzioni vengono compilate in try blocchi con finally clausole (e nessun catch blocco), funzionano correttamente con gli iteratori. Le risorse eliminabili vengono gestite correttamente durante l'esecuzione dell'iteratore:
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.");
}
Come illustrato nell'esempio precedente, la risorsa acquisita nell'istruzione using rimane disponibile durante l'esecuzione dell'iteratore, anche quando l'iteratore sospende e riprende l'esecuzione in corrispondenza yield return delle istruzioni. La risorsa viene eliminata al termine dell'iteratore (raggiungendo la fine o tramite yield break) o quando l'iteratore stesso viene eliminato (ad esempio, quando il chiamante si interrompe all'inizio dell'enumerazione).
Esecuzione di un iteratore
La chiamata di un iteratore non porta alla sua esecuzione immediata, come illustrato nell'esempio seguente:
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.
Come illustrato nell'esempio precedente, quando si inizia a scorrere il risultato di un iteratore, un iteratore viene eseguito fino a quando non viene raggiunta la prima istruzione yield return. L'esecuzione di un iteratore viene quindi sospesa e il chiamante ottiene il primo valore di iterazione e lo elabora. In ogni iterazione successiva, l'esecuzione di un iteratore riprende dopo l'istruzione yield return che ha causato la sospensione precedente e continua fino al raggiungimento dell'istruzione yield return successiva. L'iterazione viene completata quando il controllo raggiunge la fine di un iteratore o di un'istruzione yield break.
Specifiche del linguaggio C#
Per altre informazioni, vedere la sezione Istruzione yield della specifica del linguaggio C#.