Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
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.
Le langage C# documente la version la plus récente de la langue C#. Il contient également la documentation initiale des fonctionnalités dans les préversions publiques pour la prochaine version du langage.
La documentation identifie toute fonctionnalité introduite en premier dans les trois dernières versions de la langue ou dans les préversions publiques actuelles.
Conseil / Astuce
Pour savoir quand une fonctionnalité a été introduite en C#, consultez l’article sur l’historique des versions du langage C#.
Dans les exemples précédents, le type de retour des itérateurs est IEnumerable<T>. Dans les cas non générés, utilisez IEnumerable le 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 d’instance ou d’
GetEnumeratorpour 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 utiliseryielddans des méthodes avec des blocsunsafe, mais pas dans le blocunsafe. -
yield returnetyield breakne peut pas être utilisé dans les blocs catch et enfin, ou dans les blocs try avec un bloc correspondant.catchLesyield returninstructions etyield breakles instructions peuvent être utilisées dans untrybloc sanscatchblocs, qu’unfinallybloc.
using instructions dans les itérateurs
Vous pouvez utiliser using des instructions dans les méthodes d’itérateur. Étant donné que using les instructions sont compilées en try blocs avec finally des clauses (et pas catch de blocs), elles fonctionnent correctement avec des itérateurs. Les ressources jetables sont correctement gérées tout au long de l’exécution de l’itérateur :
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.");
}
Comme l’illustre l’exemple précédent, la ressource acquise dans l’instruction using reste disponible tout au long de l’exécution de l’itérateur, même lorsque l’itérateur suspend et reprend l’exécution aux yield return instructions. La ressource est supprimée lorsque l’itérateur se termine (soit en atteignant la fin ou via yield break) soit lorsque l’itérateur lui-même est supprimé (par exemple, lorsque l’appelant s’interrompt en début d’énumération).
Exécution d’un itérateur
L’appel d’un itérateur ne l’exécute pas immédiatement, comme l’illustre 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 l’illustre l’exemple précédent, lorsque vous commencez à itérer sur le résultat d’un itérateur, l’itérateur s’exécute jusqu’à ce que la première yield return instruction soit atteinte. Ensuite, l’exécution de l’itérateur est suspendue et l’appelant obtient la première valeur d’itération et la traite. Sur chaque itération suivante, l’exécution de l’itérateur reprend après l’instruction yield return qui a provoqué la suspension précédente et se poursuit jusqu’à ce que l’instruction suivante yield return 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#.