Compartir a través de


Instrucción yield: proporcione el siguiente elemento.

Use la instrucción yield en un iterador para proporcionar el siguiente valor o indicar el final de la iteración. La instrucción yield tiene las dos formas siguientes:

  • yield return: para proporcionar el siguiente valor en la iteración, como se muestra en el siguiente ejemplo:

    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: para indicar explícitamente el final de la iteración, como se muestra en el siguiente ejemplo:

    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;
            }
        }
    }
    

    La iteración finaliza también cuando el control llega al final de un iterador.

En los ejemplos anteriores, el tipo de valor devuelto de los iteradores es IEnumerable<T> (en casos no genéricos, use IEnumerable como tipo de valor devuelto de un iterador). También se puede usar IAsyncEnumerable<T> como tipo de valor devuelto de un iterador. Esto hace que el iterador sea asincrónico. Use la instrucción await foreach para iterar el resultado del iterador, como se muestra en el siguiente ejemplo:

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 pueden ser también el tipo de valor devuelto de un iterador. Use tipos de valor devuelto cuando implemente el método GetEnumerator en los siguientes escenarios:

  • Al diseñar el tipo que implementa la interfaz IEnumerable<T> o IEnumerable.

  • Al agregar una instancia o un método de extensión GetEnumerator para habilitar la iteración por la instancia del tipo con la instrucción foreach, como se muestra en el siguiente ejemplo:

    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;
        }
    }
    

Las yield instrucciones no pueden usarse en:

  • Métodos con parámetros in, ref o out
  • Expresiones lambda y métodos anónimos
  • Bloques no seguros. Antes de C# 13, yield no era válido en ningún método con un bloque unsafe. A partir de C# 13, puede usar yield en métodos con bloques unsafe, pero no en el bloque unsafe.
  • yield return y yield break no se pueden usar en bloques catch y finally , o en los bloques try con un bloque correspondiente catch . Las yield return instrucciones y yield break se pueden usar en un try bloque sin catch bloques, solo un finally bloque.

Ejecución de un iterador

La llamada de un iterador no hace que se ejecute de inmediato, como se muestra en el siguiente ejemplo:

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.

Como se aprecia en el ejemplo anterior, cuando se empieza a iterar por el resultado de un iterador, se ejecuta un iterador hasta que se alcanza la primera instrucción yield return. Tras ello, se suspende la ejecución de un iterador, el autor de la llamada obtiene el primer valor de iteración y lo procesa. En cada iteración posterior, la ejecución de un iterador se reanuda después de la instrucción yield return que provocó la suspensión anterior, y continúa hasta que se alcanza la siguiente instrucción yield return. La iteración se completa cuando el control llega al final de un iterador o una instrucción yield break.

Especificación del lenguaje C#

Para obtener más información, vea la sección La declaración yield de Especificación del lenguaje C#.

Consulte también