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’instruction foreach, 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 bloc unsafe. À compter de C# 13, vous pouvez utiliser yield dans des méthodes avec des blocs unsafe , mais pas dans le bloc unsafe.
  • yield return et yield 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#.

Voir aussi