Dado este código:
IEnumerable<object> FilteredList()
{
foreach( object item in FullList )
{
if( IsItemInPartialList( item ) )
yield return item;
}
}
¿Por qué no debería simplemente codificarlo de esta manera ?:
IEnumerable<object> FilteredList()
{
var list = new List<object>();
foreach( object item in FullList )
{
if( IsItemInPartialList( item ) )
list.Add(item);
}
return list;
}
Entiendo lo que hace la yield
palabra clave. Le dice al compilador que construya cierto tipo de cosas (un iterador). ¿Pero por qué usarlo? Además de ser un poco menos de código, ¿qué hace por mí?
FullList.Where(IsItemInPartialList)
:)Respuestas:
El uso
yield
hace que la colección sea perezosa.Digamos que solo necesita los primeros cinco elementos. A su manera, tengo que recorrer toda la lista para obtener los primeros cinco elementos. Con
yield
, solo recorro los primeros cinco elementos.fuente
FullList.Where(IsItemInPartialList)
será igual de perezoso. Solo que requiere mucho menos código personalizado --- gunk --- generado por el compilador. Y menos tiempo para desarrolladores escribiendo y manteniendo. (Por supuesto, eso fue sólo este ejemplo)yield return
) siempre que sea posible.El beneficio de los bloques iteradores es que funcionan perezosamente. Entonces puedes escribir un método de filtrado como este:
Eso le permitirá filtrar una transmisión todo el tiempo que desee, sin almacenar más de un elemento a la vez. Si solo necesita el primer valor de la secuencia devuelta, por ejemplo, ¿por qué querría copiar todo en una nueva lista?
Como otro ejemplo, puede crear fácilmente una secuencia infinita utilizando bloques iteradores. Por ejemplo, aquí hay una secuencia de números aleatorios:
¿Cómo almacenarías una secuencia infinita en una lista?
Mi serie de blogs Edulinq ofrece una implementación de ejemplo de LINQ to Objects que hace un uso intensivo de los bloques iteradores. LINQ es fundamentalmente vago donde puede estar, y poner las cosas en una lista simplemente no funciona de esa manera.
fuente
RandomSequence
o no. Para mí, IEnumerable significa, en primer lugar, que puedo iterar con foreach, pero esto obviamente conduciría a un bucle infinito aquí. Consideraría esto un mal uso bastante peligroso del concepto IEnumerable, pero YMMV.IEnumerable<BigInteger>
representación de la secuencia de Fibonacci, por ejemplo. Puede usarloforeach
, pero nadaIEnumerable<T>
garantiza que será finito.Con el código de "lista", debe procesar la lista completa antes de poder pasar al siguiente paso. La versión de "rendimiento" pasa el artículo procesado inmediatamente al siguiente paso. Si ese "próximo paso" contiene un ".Take (10)", entonces la versión "rendimiento" solo procesará los primeros 10 elementos y se olvidará del resto. El código de "lista" habría procesado todo.
Esto significa que ve la mayor diferencia cuando necesita hacer mucho procesamiento y / o tiene largas listas de elementos para procesar.
fuente
Puede usar
yield
para devolver artículos que no están en una lista. Aquí hay una pequeña muestra que podría recorrer infinitamente una lista hasta que se cancele.Esto escribe
... etc. a la consola hasta que se cancele.
fuente
Cuando el código anterior se usa para recorrer FilteredList () y suponiendo que item.Name == "James" se satisfará en el segundo elemento de la lista, el método que se use
yield
rendirá dos veces. Este es un comportamiento perezoso.Donde como el método que usa list agregará todos los n objetos a la lista y pasará la lista completa al método de llamada.
Este es exactamente un caso de uso donde se puede resaltar la diferencia entre IEnumerable e IList.
fuente
El mejor ejemplo del mundo real que he visto para el uso
yield
sería calcular una secuencia de Fibonacci.Considere el siguiente código:
Esto devolverá:
Esto es bueno porque le permite calcular una serie infinita de forma rápida y sencilla, lo que le permite utilizar las extensiones de Linq y consultar solo lo que necesita.
fuente
A veces es útil, a veces no. Si el conjunto completo de datos debe ser examinado y devuelto, no habrá ningún beneficio en el uso del rendimiento porque todo lo que hizo fue introducir gastos generales.
Cuando el rendimiento realmente brilla es cuando solo se devuelve un conjunto parcial. Creo que el mejor ejemplo es la clasificación. Suponga que tiene una lista de objetos que contienen una fecha y un monto en dólares de este año y le gustaría ver los primeros (5) registros del año.
Para lograr esto, la lista debe ordenarse de forma ascendente por fecha y luego tomar los primeros 5. Si esto se hiciera sin rendimiento, toda la lista tendría que ser ordenada, hasta asegurarse de que las dos últimas fechas estuvieran en orden.
Sin embargo, con el rendimiento, una vez que se han establecido los primeros 5 elementos, la clasificación se detiene y los resultados están disponibles. Esto puede ahorrar una gran cantidad de tiempo.
fuente
La declaración de devolución de rendimiento le permite devolver solo un artículo a la vez. Está recopilando todos los elementos en una lista y está volviendo a devolver esa lista, que es una sobrecarga de memoria.
fuente