entre ejemplos diferencia and c# linq list ienumerable

c# - ejemplos - IEnumerable vs List-¿Qué usar? ¿Cómo trabajan?



difference between ienumerable and list (9)

Tengo algunas dudas sobre cómo funcionan los Enumerators y LINQ. Considere estos dos simples selecciones:

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 pareciera 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*/ }

  1. Noté que si uso IEnumerable , cuando IEnumerable e inspecciono "sel", que en ese caso es IEnumerable, tiene algunos miembros interesantes: "inner", "outer", "innerKeySelector" y "outerKeySelector", estos últimos 2 parecen ser delegados. El miembro "interno" no tiene instancias de "Animal", sino instancias de "Especie", lo cual fue muy extraño para mí. El miembro "externo" contiene instancias de "Animal". Supongo que los dos delegados determinan qué entra y qué sale de ello.

  2. Noté que si uso "Distinct", el "inner" contiene 6 elementos (esto es incorrecto, ya que solo 2 son Distinct), pero el "exterior" contiene los valores correctos. De nuevo, probablemente los métodos delegados determinen esto, pero esto es un poco más de lo que sé sobre IEnumerable.

  3. Lo más importante, ¿cuál de las dos opciones es la mejor en cuanto a rendimiento?

¿La conversión malvada de la lista vía .ToList() ?

¿O tal vez usando directamente el enumerador?

Si puede, también explique un poco o lance algunos enlaces que expliquen este uso de IEnumerable.


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. Entonces, 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 una implementación específica.


Compartiré un concepto mal usado que caí en un día:

var names = new List<string> {"mercedes", "mazda", "bmw", "fiat", "ferrari"}; var startingWith_M = names.Where(x => x.StartsWith("m")); var startingWith_F = names.Where(x => x.StartsWith("f")); // updating existing list names[0] = "ford"; // Guess what should be printed before continuing print( startingWith_M.ToList() ); print( startingWith_F.ToList() );

Resultado Esperado

// I was expecting print( startingWith_M.ToList() ); // mercedes, mazda print( startingWith_F.ToList() ); // fiat, ferrari

Resultado actual

// what printed actualy print( startingWith_M.ToList() ); // mazda print( startingWith_F.ToList() ); // ford, fiat, ferrari

Explicación

Según otras respuestas, la evaluación del resultado se aplazó hasta llamar a ToList o métodos de invocación similares, por ejemplo, ToArray .

Entonces puedo reescribir el código en este caso como:

var names = new List<string> {"mercedes", "mazda", "bmw", "fiat", "ferrari"}; // updating existing list names[0] = "ford"; // before calling ToList directly var startingWith_M = names.Where(x => x.StartsWith("m")); var startingWith_F = names.Where(x => x.StartsWith("f")); print( startingWith_M.ToList() ); print( startingWith_F.ToList() );

Jugar cerca

https://repl.it/E8Ki/0



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 en espera hasta que sea necesaria (también conocida como carga perezosa).

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 se apega a IEnumerable, tiene la ventaja de que el programa no realiza ningún trabajo hasta que realmente se requiere.


Lo más importante es darse cuenta de que, utilizando Linq, la consulta no se evalúa de inmediato. Solo se ejecuta como parte de la iteración a través del IEnumerable<T> resultante en un foreach , eso es lo que están haciendo todos los delegados extraños.

Por lo tanto, el primer ejemplo evalúa la consulta inmediatamente llamando a 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 a la vez (por ejemplo, está mutando las estructuras que está consultando más adelante, o si no desea que la iteración sobre el IEnumerable<T> tarde mucho tiempo) use una lista . De lo contrario, utilice un IEnumerable<T> . El valor predeterminado debe ser usar la evaluación a pedido en el segundo ejemplo, ya que generalmente utiliza menos memoria, a menos que haya una razón específica para almacenar los resultados en una lista.



Si todo lo que desea hacer es enumerarlos, use IEnumerable .

Sin embargo, tenga en cuenta que cambiar la colección original que se está enumerando es una operación peligrosa; en este caso, primero ToList hacer una ToList de ToList . Esto creará un nuevo elemento de lista para cada elemento en la memoria, enumerando el IEnumerable y, por lo tanto, IEnumerable menos rendimiento si solo se enumera una vez, pero es más seguro y, a veces, los métodos de List son útiles (por ejemplo, en el acceso aleatorio).


Una clase que implementa IEnumerable permite usar la sintaxis de foreach .

Básicamente tiene un método para obtener el siguiente artículo en la colección. No necesita que toda la colección esté en la memoria y no sabe cuántos elementos hay en ella, ya que cada uno sigue obteniendo el siguiente elemento hasta que se agote.

Esto puede ser muy útil en ciertas circunstancias, por ejemplo, en una tabla de base de datos masiva que no desea copiar todo en la memoria antes de comenzar a procesar las filas.

Ahora la List implementa IEnumerable , pero representa la colección completa en la memoria. Si tiene un IEnumerable y llama a .ToList() , creará 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 se itera mediante el uso de foreach . Una IEnumerable IEnumerable se ejecuta cuando se itera el foreach , pero puede forzarlo a iterar antes usando .ToList() .

Esto es lo que quiero decir:

var things = from item in BigDatabaseCall() where .... select item; // this will iterate through the entire linq statement: int count = things.Count(); // this will stop after iterating the first one, but will execute the linq again bool hasAnyRecs = things.Any(); // this will execute the linq statement *again* foreach( var thing in things ) ... // this will copy the results to a list in memory var list = things.ToList() // this won''t iterate through again, the list knows how many items are in it int count2 = list.Count(); // this won''t execute the linq statement - we have it copied to the list foreach( var thing in list ) ...


IEnumerable describe el comportamiento, mientras que List es una implementación de ese comportamiento. Cuando usa IEnumerable , le da al compilador la oportunidad de diferir el trabajo hasta más tarde, posiblemente optimizando en el camino. Si usa ToList (), obliga al compilador a verificar los resultados de inmediato.

Siempre que estoy "apilando" las expresiones LINQ, uso IEnumerable , porque al solo especificar el comportamiento, le doy a LINQ la oportunidad de diferir la evaluación y posiblemente optimizar el programa. ¿Recuerda que LINQ no genera el SQL para consultar la base de datos hasta que la enumere? Considera esto:

public IEnumerable<Animals> AllSpotted() { return from a in Zoo.Animals where a.coat.HasSpots == true select a; } public IEnumerable<Animals> Feline(IEnumerable<Animals> sample) { return from a in sample where a.race.Family == "Felidae" select a; } public IEnumerable<Animals> Canine(IEnumerable<Animals> sample) { return from a in sample where a.race.Family == "Canidae" select a; }

Ahora tiene un método que selecciona una muestra inicial ("AllSpotted"), más algunos filtros. Así que ahora puedes hacer esto:

var Leopards = Feline(AllSpotted()); var Hyenas = Canine(AllSpotted());

Entonces, ¿es más rápido usar Lista sobre IEnumerable ? Solo si desea evitar que una consulta se ejecute más de una vez. Pero, ¿es mejor en general? Bien en lo anterior, los leopardos y las hienas se convierten en consultas SQL individuales cada una , y la base de datos solo devuelve las filas que son relevantes. Pero si hubiésemos devuelto una Lista de AllSpotted() , entonces puede que se ejecute más lentamente porque la base de datos podría devolver muchos más datos de los que realmente se necesitan, y desperdiciamos los ciclos haciendo el filtrado en el cliente.

En un programa, puede ser mejor aplazar la conversión de su consulta a una lista hasta el final, así que si voy a enumerar a través de Leopardos y hienas más de una vez, haría esto:

List<Animals> Leopards = Feline(AllSpotted()).ToList(); List<Animals> Hyenas = Canine(AllSpotted()).ToList();