Pregunta IEnumerable vs List - ¿Qué usar? ¿Cómo trabajan?


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í misma no es tan importante. Lo que quiero preguntar es esto:

foreach (Animal animal in sel) { /*do stuff*/ }
  1. Me di cuenta de que si uso IEnumerable, cuando depuro 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 "Animal", sino instancias de "Especies", lo cual fue muy extraño para mí. El miembro "externo" contiene instancias "Animal". Supongo que los dos delegados determinan qué entra y qué sale de él.

  2. Noté que si uso "Distinct", el "interno" contiene 6 elementos (esto es incorrecto ya que solo 2 son Distinct), pero el "exterior" contiene los valores correctos. Una vez más, 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 de la lista del mal a través de .ToList()?

¿O tal vez usar el enumerador directamente?

Si puede, explique un poco o envíe algunos enlaces que explican este uso de IEnumerable.


534
2017-09-02 15:05


origen


Respuestas:


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

Cuando estoy "apilando" expresiones LINQ, uso IEnumerable, porque al especificar solo el comportamiento le doy a LINQ la oportunidad de diferir la evaluación y posiblemente optimizar el programa. ¿Recuerda cómo 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. Entonces ahora puedes hacer esto:

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

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

En un programa, puede ser mejor diferir 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();

593
2017-09-02 15:36



Una clase que implementa IEnumerable le permite usar el foreach sintaxis.

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

Esto puede ser muy útil en determinadas 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 implementos IEnumerable, pero representa toda la colección en la memoria. Si tienes un IEnumerable y tu llamas .ToList() usted crea una nueva lista con los contenidos 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 el foreach. Un IEnumerable La instrucción linq se ejecuta cuando itera el foreach, pero puedes 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 ) ...

108
2017-09-02 15:24



Hay un artículo muy bueno escrito por: TechBlog de Claudio Bernasconi aquí: Cuándo usar IEnumerable, ICollection, IList y List

Aquí algunos puntos básicos apuntan a escenarios y funciones:

enter image description here enter image description here


99
2018-06-04 14:07



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

Entonces, el primer ejemplo evalúa la consulta inmediatamente llamando ToList y poner 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 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 la iteración sobre el IEnumerable<T> tomar un largo tiempo) use una lista. De lo contrario, use una IEnumerable<T>. El valor predeterminado debería 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.


64
2017-09-02 15:08



Nadie mencionó una diferencia crucial, irónicamente respondida en una pregunta cerrada como duplicado de esto.

IEnumerable es de solo lectura y List no.

Ver Diferencia práctica entre List e IEnumerable


60
2017-12-09 08:30



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 que espera hasta que se necesita (también conocida como carga lenta).

Si llama a ToList, la consulta se ejecutará o se "materializará" como me gustaría decir.

Hay ventajas y desventajas para ambos. Si llama a ToList, puede eliminar algún misterio sobre cuándo se ejecuta la consulta. Si te apegas a IEnumerable, obtienes la ventaja de que el programa no hace ningún trabajo hasta que realmente se requiera.


34
2017-09-02 15:13



Si todo lo que quiere hacer es enumerarlos, use la IEnumerable.

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


15
2017-09-02 15:09



Compartiré un concepto mal utilizado en el 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 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


10
2017-11-26 08:03



Además de todas las respuestas publicadas anteriormente, aquí están mis dos centavos. Hay muchos otros tipos además de List que implementan IEnumerable tales como ICollection, ArrayList, etc. Por lo tanto, 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 y no en una implementación específica.


5
2018-06-15 23:22