Tengo algunas dudas sobre cómo funcionan los enumeradores y LINQ. Considere estas dos selecciones simples:
List<Animal> sel = (from animal in Animals
join race in Species
on animal.SpeciesKey equals race.SpeciesKey
select animal).Distinct().ToList();
o
IEnumerable<Animal> sel = (from animal in Animals
join race in Species
on animal.SpeciesKey equals race.SpeciesKey
select animal).Distinct();
Cambié los nombres de mis objetos originales para que parezca un ejemplo más genérico. La consulta en sí no es tan importante. Lo que quiero preguntar es esto:
foreach (Animal animal in sel) { /*do stuff*/ }
Me di cuenta de que si utilizo
IEnumerable
, cuando depuro e inspecciono "sel", que en ese caso es el IEnumerable, tiene algunos miembros interesantes: "inner", "external", "innerKeySelector" y "externalKeySelector", estos últimos 2 aparecen ser delegados El miembro "interno" no tiene instancias de "Animal", sino instancias de "Especies", lo cual fue muy extraño para mí. El miembro "externo" contiene instancias "animales". Supongo que los dos delegados determinan qué entra y qué sale.Me di cuenta de que si uso "Distinct", el "interno" contiene 6 elementos (esto es incorrecto ya que solo 2 son distintos), pero el "externo" contiene los valores correctos. De nuevo, probablemente los métodos delegados determinan esto, pero esto es un poco más de lo que sé sobre IEnumerable.
Lo más importante, ¿cuál de las dos opciones es la mejor en cuanto a rendimiento?
La conversión Lista mal a través de .ToList()
?
¿O tal vez usando el enumerador directamente?
Si puede, explique también un poco o arroje algunos enlaces que expliquen este uso de IEnumerable.
fuente
IEnumerable<T>
será LINQ-To-Objects después de la primera parte, lo que significa que todos los vistos tendrían que ser devueltos para ejecutar Feline. Por otro lado, unIQuertable<T>
permitirá que la consulta sea refinada, derribando solo felinos manchados.Hay un muy buen artículo escrito por: Claudio Bernasconi TechBlog aquí: Cuándo usar IEnumerable, ICollection, IList and List
Aquí algunos puntos básicos sobre escenarios y funciones:
fuente
List
es una implementación deIList
y como tal tiene una funcionalidad adicional en la parte superior de los deIList
(por ejemploSort
,Find
,InsertRange
). Si te obligas a usarIList
másList
, pierdes estos métodos que puedes necesitarIReadOnlyCollection<T>
[]
aquí también.Una clase que implementa le
IEnumerable
permite usar laforeach
sintaxis.Básicamente tiene un método para obtener el siguiente elemento de la colección. No necesita que toda la colección esté en la memoria y no sabe cuántos elementos contiene,
foreach
solo sigue obteniendo el siguiente elemento hasta que se agota.Esto puede ser muy útil en ciertas circunstancias, por ejemplo, en una tabla de base de datos masiva, no desea copiar todo en la memoria antes de comenzar a procesar las filas.
Ahora
List
implementaIEnumerable
, pero representa la colección completa en la memoria. Si tiene unIEnumerable
y llama.ToList()
, crea una nueva lista con el contenido de la enumeración en la memoria.Su expresión linq devuelve una enumeración y, de forma predeterminada, la expresión se ejecuta cuando itera utilizando
foreach
. UnaIEnumerable
instrucción linq se ejecuta cuando iteraforeach
, pero puede forzarla a iterar antes usando.ToList()
.Esto es lo que quiero decir:
fuente
foreach
mientras List irá por dicho índice. Ahora, si quisiera saber el conteo / duración dething
antemano, IEnumerable no ayudará, ¿verdad?List
eArray
implementarIEnumerable
. Puede considerarseIEnumerable
como el mínimo común denominador que funciona tanto en colecciones de memoria como en grandes que obtienen un elemento a la vez. Cuando llameIEnumerable.Count()
, puede estar llamando a una.Length
propiedad rápida o revisando toda la colección; el punto es que conIEnumerable
usted no sabe. Eso puede ser un problema, pero si solo va aforeach
hacerlo, entonces no le importa: su código funcionará con unoArray
oDataReader
el mismo.Nadie mencionó una diferencia crucial, irónicamente respondió a una pregunta cerrada como un duplicado de esto.
Consulte la diferencia práctica entre List y IEnumerable
fuente
Lo más importante a tener en cuenta es que, utilizando Linq, la consulta no se evalúa de inmediato. Solo se ejecuta como parte de iterar a través del resultado
IEnumerable<T>
en unforeach
- eso es lo que están haciendo todos los delegados extraños.Entonces, el primer ejemplo evalúa la consulta inmediatamente llamando
ToList
y colocando los resultados de la consulta en una lista.El segundo ejemplo devuelve un
IEnumerable<T>
que contiene toda la información necesaria para ejecutar la consulta más adelante.En términos de rendimiento, la respuesta es que depende . Si necesita que los resultados se evalúen de inmediato (por ejemplo, está mutando las estructuras que está consultando más adelante, o si no desea que la iteración
IEnumerable<T>
tome mucho tiempo) use una lista. De lo contrario, use unIEnumerable<T>
. El valor predeterminado debería ser usar la evaluación a pedido en el segundo ejemplo, ya que generalmente usa menos memoria, a menos que haya una razón específica para almacenar los resultados en una lista.fuente
Join
trabajo: el interior y el exterior son los dos lados de la unión. En general, no se preocupe por lo que hay realmente en elIEnumerables
, ya que será completamente diferente de su código real. Solo preocúpate por la salida real cuando iteras sobre ella :)La ventaja de IEnumerable es la ejecución diferida (generalmente con bases de datos). La consulta no se ejecutará hasta que realmente recorra los datos. Es una consulta esperando hasta que sea necesaria (también conocida como carga diferida).
Si llama a ToList, la consulta se ejecutará o se "materializará" como me gusta decir.
Hay pros y contras para ambos. Si llama a ToList, puede eliminar algún misterio sobre cuándo se ejecuta la consulta. Si te quedas con IEnumerable, obtienes la ventaja de que el programa no hace ningún trabajo hasta que realmente sea necesario.
fuente
Compartiré un concepto mal utilizado en el que caí un día:
Resultado Esperado
Resultado actual
Explicación
Según otras respuestas, la evaluación del resultado fue diferida hasta la llamada
ToList
o métodos de invocación similares, por ejemploToArray
.Entonces puedo reescribir el código en este caso como:
Jugar alrededor
https://repl.it/E8Ki/0
fuente
IEnumerable
funciona, pero ahora no es más, ya que sé cómo;)Si todo lo que quiere hacer es enumerarlos, use el
IEnumerable
.Sin embargo, tenga en cuenta que cambiar la colección original que se enumera es una operación peligrosa; en este caso,
ToList
primero querrá hacerlo . Esto creará un nuevo elemento de lista para cada elemento en la memoria, enumerandoIEnumerable
y, por lo tanto, será menos eficaz si solo enumera una vez, pero es más seguro y, a veces, losList
métodos son útiles (por ejemplo, en acceso aleatorio).fuente
IEnumerable
, crear primero una lista (e iterar sobre eso) significa iterar sobre los elementos dos veces . Entonces, a menos que desee realizar operaciones que sean más eficientes en la lista, esto realmente significa un menor rendimiento..ToList()
ayuda aquí;)Además de todas las respuestas publicadas anteriormente, aquí están mis dos centavos. Hay muchos otros tipos además de List que implementa IEnumerable como ICollection, ArrayList, etc. Así que si tenemos IEnumerable como parámetro de cualquier método, podemos pasar cualquier tipo de colección a la función. Es decir, podemos tener un método para operar en abstracción, no en ninguna implementación específica.
fuente
Hay muchos casos (como una lista infinita o una lista muy grande) donde IEnumerable no se puede transformar en una Lista. Los ejemplos más obvios son todos los números primos, todos los usuarios de Facebook con sus detalles o todos los artículos en eBay.
La diferencia es que los objetos "Lista" se almacenan "aquí y ahora", mientras que los objetos "IEnumerable" funcionan "solo uno a la vez". Entonces, si estoy revisando todos los elementos en eBay, uno a la vez sería algo que incluso una computadora pequeña puede manejar, pero ".ToList ()" seguramente me dejaría sin memoria, sin importar cuán grande fuera mi computadora. Ninguna computadora puede por sí misma contener y manejar una cantidad tan grande de datos.
[Editar] - No hace falta decir que no es "ni esto ni aquello". a menudo tendría sentido usar una lista y un IEnumerable en la misma clase. Ninguna computadora en el mundo podría enumerar todos los números primos, porque por definición esto requeriría una cantidad infinita de memoria. Pero podría pensar fácilmente en un
class PrimeContainer
que contiene unIEnumerable<long> primes
, que por razones obvias también contiene unSortedList<long> _primes
. Todos los números primos calculados hasta ahora. el próximo primo que se comprobará solo se ejecutará contra los primos existentes (hasta la raíz cuadrada). De esa manera, obtienes ambos: números primos uno a la vez (IEnumerable) y una buena lista de "números primos hasta ahora", que es una aproximación bastante buena de toda la lista (infinita).fuente