Nota
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
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
GetEnumeratorpara habilitar la iteración por la instancia del tipo con la instrucciónforeach, 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,
yieldno era válido en ningún método con un bloqueunsafe. A partir de C# 13, puede usaryielden métodos con bloquesunsafe, pero no en el bloqueunsafe. -
yield returnyyield breakno se pueden usar en bloques catch y finally , o en los bloques try con un bloque correspondientecatch. Lasyield returninstrucciones yyield breakse pueden usar en untrybloque sincatchbloques, solo unfinallybloque.
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
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#.