Nota
O acceso a esta páxina require autorización. Pode tentar iniciar sesión ou modificar os directorios.
O acceso a esta páxina require autorización. Pode tentar modificar os directorios.
Elija un iterador cuando desee devolver una secuencia cuyos elementos llamadores pueden consumir uno a uno, sin crear primero el resultado completo en una colección. Este enfoque se adapta a valores generados, filtros con flujo de control personalizado y secuencias que los llamantes podrían dejar de leer antes de tiempo. En comparación con una recopilación completa, un iterador cambia cuando se realiza el trabajo, no solo en función de cómo se escriba.
Tip
En este artículo se enseñan iteradores sincrónicos para desarrolladores que conocen métodos, bucles y colecciones. Para obtener una sintaxis completa yield y las restricciones, consulte la referencia de la yield sentencia.
Un método de iterador genera una secuencia de valores de uno en uno. Este artículo se centra en los métodos de iterador que devuelven IEnumerable<T>, cuyos valores el código que llama suele consumir con una instrucción foreach.
Generar elementos con yield return
Utilice yield return para proporcionar el siguiente elemento de la secuencia. El iterador siguiente selecciona lecturas de temperatura aceptables de un sensor. En este escenario, -1 marca el final de los datos del sensor.
int[] readings = [21, 27, 24, -1, 19];
IEnumerable<int> acceptedReadings =
SelectTemperatures(readings, maximum: 25);
Console.WriteLine("Sequence created.");
foreach (int temperature in acceptedReadings)
{
Console.WriteLine($"Accepted: {temperature}°C");
}
static IEnumerable<int> SelectTemperatures(
IEnumerable<int> readings,
int maximum)
{
foreach (int reading in readings)
{
Console.WriteLine($"Checking {reading}°C");
if (reading == -1)
{
yield break;
}
if (reading <= maximum)
{
yield return reading;
}
}
}
SelectTemperatures devuelve IEnumerable<int>, que representa una secuencia de int valores. Cada yield return reading proporciona un valor al bucle foreach. Una vez que el llamante haya procesado ese valor, el iterador continúa con la siguiente entrada.
Utilice la forma genérica IEnumerable<T> para la mayor parte del código C# moderno. El argumento type, como int, indica a los autores de llamada qué tipo de elemento contiene la secuencia.
Finalizar la secuencia con yield break
Utilice yield break para finalizar un iterador antes de que el control llegue al final del método. En el ejemplo, el valor -1 de sentinel significa que no hay más lecturas disponibles:
if (reading == -1)
{
yield break;
}
El iterador también termina naturalmente cuando la ejecución llega al final de su cuerpo. Utiliza yield break cuando una condición deba detener la generación de elementos de forma inmediata.
Consumir un iterador con foreach
Un iterador expone la misma IEnumerable<T> forma que muchas colecciones .NET. Los llamantes pueden utilizar foreach sin saber cómo la secuencia genera sus elementos:
foreach (int temperature in SelectTemperatures(readings, 25))
{
Console.WriteLine($"Accepted: {temperature}°C");
}
El llamador solicita cada elemento a medida que avanza el bucle. El iterador mantiene su posición entre solicitudes y continúa tras el yield return anterior.
Comprender la producción diferida de elementos
Llamar a un método iterador no ejecuta su cuerpo inmediatamente. El método comienza a generar elementos cuando el código que llama comienza la enumeración. Este comportamiento se denomina ejecución diferida.
El ejemplo imprime Sequence created. antes de cualquier mensaje Checking .... El iterador comienza a comprobar las lecturas solo cuando el bucle foreach solicita su primer elemento.
La ejecución diferida tiene efectos prácticos:
- El llamador puede comenzar el procesamiento antes de que el iterador genere todos los elementos.
- El iterador puede detenerse temprano con
yield break. - El código que llama que nunca enumera la secuencia no ejecuta el cuerpo del iterador.
- Cada nueva enumeración vuelve a recorrer el iterador.
Deje clara la ejecución diferida en el propósito y la documentación del método. Los lectores no pueden deducir el momento de ejecución solo a partir de IEnumerable<T> porque tanto los iteradores como las colecciones pueden exponer esa interfaz. Mantenga la producción de elementos libre de efectos secundarios no relacionados, ya que quienes llaman pueden enumerar la secuencia más adelante o más de una vez. Los mensajes de diagnóstico del ejemplo muestran esa temporización.
Elegir un iterador o una colección
Devuelve un iterador cuando los valores llegan de forma natural uno a uno o cuando el autor de la llamada no necesita el resultado completo inmediatamente. Devuelve una colección completa cuando los llamantes necesiten una instantánea estable, un acceso indexado o una enumeración repetida sin volver a calcular los elementos.
Ambos enfoques pueden devolver los mismos elementos. El iterador deja la generación de elementos bajo el control del llamante; un método que crea una colección completa esa tarea antes de devolver un resultado.
Utilice un bucle normal cuando desee realizar una acción para cada elemento sin devolver una secuencia. Para un filtro o transformación estándar, prefiera una operación LINQ como Where o Select; escriba un iterador cuando la lógica personalizada de producción de elementos sea más clara como una serie de instrucciones.
Los métodos de iterador se componen con LINQ porque ambos usan IEnumerable<T>. Por ejemplo, un llamador puede agregar otro filtro:
var comfortableReadings = SelectTemperatures(readings, 25)
.Where(static temperature => temperature >= 18);
El iterador y la operación LINQ permanecen diferidos hasta que el código enumera comfortableReadings.
Para las secuencias asincrónicas, consulte Generación y consumo de flujos asincrónicos. Para los patrones de enumeración personalizados y todas las yield restricciones, use la referencia de lenguaje.