Compartir a través de


Instrucción yield: proporcione el siguiente elemento.

Use la yield instrucción en un iterador para proporcionar el siguiente valor o indicar el final de una 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.

La referencia del lenguaje C# documenta la versión publicada más recientemente del lenguaje C#. También contiene documentación inicial sobre las características de las versiones preliminares públicas de la próxima versión del lenguaje.

La documentación identifica cualquier característica introducida por primera vez en las últimas tres versiones del idioma o en las versiones preliminares públicas actuales.

Sugerencia

Para buscar cuándo se introdujo por primera vez una característica en C#, consulte el artículo sobre el historial de versiones del lenguaje C#.

En los ejemplos anteriores, el tipo de valor devuelto de 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ónGetEnumerator 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.

using instrucciones en iteradores

Puede usar instruccionesusing en métodos de iterador. Dado que using las instrucciones se compilan en try bloques con finally cláusulas (y sin catch bloques), funcionan correctamente con iteradores. Los recursos descartables se administran correctamente durante la ejecución del iterador:

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.");
}

Como se muestra en el ejemplo anterior, el recurso adquirido en la using instrucción permanece disponible durante la ejecución del iterador, incluso cuando el iterador suspende y reanuda la ejecución en yield return instrucciones. El recurso se elimina cuando se completa el iterador (ya sea llegando al final o a través yield breakde ) o cuando se elimina el iterador en sí (por ejemplo, cuando el autor de la llamada se interrumpe en la enumeración antes).

Ejecución de un iterador

Llamar a un iterador no lo ejecuta inmediatamente, como se muestra en el ejemplo siguiente:

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 muestra en el ejemplo anterior, cuando se empieza a iterar sobre el resultado de un iterador, el iterador se ejecuta hasta que se alcanza la primera yield return instrucción. A continuación, se suspende la ejecución del iterador y el autor de la llamada obtiene el primer valor de iteración y lo procesa. En cada iteración posterior, la ejecución del iterador se reanuda después de la yield return instrucción que provocó la suspensión anterior y continúa hasta que se alcanza la siguiente yield return instrucción. 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