yield, instruction - fournir l’élément suivant
Vous utilisez l’instruction yield
dans un itérateur pour fournir la valeur suivante ou signaler la fin d’une itération. L’instruction yield
a les deux formes suivantes :
yield return
: pour fournir la valeur suivante dans l’itération, comme le montre l’exemple suivant :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
: pour signaler explicitement la fin de l’itération, comme le montre l’exemple suivant :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’itération se termine aussi une fois que le contrôle atteint la fin d’un itérateur.
Dans les exemples précédents, le type de retour des itérateurs est IEnumerable<T> (dans les cas non génériques, utilisez IEnumerable comme type de retour d’un itérateur). Vous pouvez aussi utiliser IAsyncEnumerable<T> comme type de retour d’un itérateur. Cela rend un itérateur asynchrone. Utilisez l’ instruction await foreach
pour itérer sur le résultat de l’itérateur, comme le montre l’exemple suivant :
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> ou IEnumerator peuvent aussi être le type de retour d’un itérateur. Utilisez ces types de retour quand vous implémentez la méthode GetEnumerator
dans les scénarios suivants :
Vous concevez le type qui implémente l’interface IEnumerable<T> ou IEnumerable.
Vous ajoutez une méthode
GetEnumerator
d’instance ou d’extension pour permettre l’itération sur l’instance du type avec l’instructionforeach
, comme le montre l’exemple suivant :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; } }
Vous ne pouvez pas utiliser les instructions yield
dans :
- les méthodes avec des paramètres in, ref ou out
- les expressions lambda et les méthodes anonymes
- blocs non managés. Avant C# 13,
yield
était non valide dans n’importe quelle méthode avec un blocunsafe
. À compter de C# 13, vous pouvez utiliseryield
dans des méthodes avec des blocsunsafe
, mais pas dans le blocunsafe
. yield return
etyield break
ne peuvent pas être utilisés dans les blocs try, catch et finally.
Exécution d’un itérateur
L’appel d’un itérateur n’a pas pour effet de l’exécuter immédiatement, comme le montre l’exemple suivant :
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.
Comme le montre l’exemple précédent, quand vous commencez à itérer sur le résultat d’un itérateur, un itérateur s’exécute jusqu’à ce que la première instruction yield return
soit atteinte. Ensuite, l’exécution d’un itérateur est suspendue et l’appelant obtient la première valeur d’itération et la traite. À chaque itération suivante, l’exécution d’un itérateur reprend après l’instruction yield return
qui a provoqué la suspension précédente et continue jusqu’à ce que l’instruction yield return
suivante soit atteinte. L’itération se termine quand le contrôle atteint la fin d’un itérateur ou une instruction yield break
.
spécification du langage C#
Pour plus d’informations, consultez la section Instruction yield de la spécification du langage C#.