¿Cuál es la diferencia entre IEnumerable y Array, IList y List?

91

¿Cuál es la diferencia entre IEnumerabley Array?

¿Cuál es la diferencia entre IListy List?

Estos parecen tener la misma función.

charla
fuente

Respuestas:

101

IEnumerable proporciona solo una funcionalidad mínima "iterable". Puedes recorrer la secuencia, pero eso es todo. Esto tiene desventajas, por ejemplo, es muy ineficiente contar elementos usando IEnumerable o para obtener el enésimo elemento, pero también tiene ventajas, por ejemplo, un IEnumerable podría ser una secuencia sin fin, como la secuencia de números primos.

Array es una colección de tamaño fijo con acceso aleatorio (es decir, puede indexar en ella).

La lista es una colección de tamaño variable (es decir, puede agregar y eliminar elementos) con acceso aleatorio.

IList es una interfaz que abstrae la funcionalidad de la lista (contar, agregar, eliminar, acceso al indexador) de las diversas clases concretas como List, BindingList, ObservableCollection, etc.

itowlson
fuente
2
De acuerdo con la respuesta aceptada aquí stackoverflow.com/questions/1826658/… . Count on IEnumerable es casi lo mismo porque intenta llamar a Count on ICollection
Karsten
15
Si y no. El método de extensión Count () comprueba si IEnumerable también es una ICollection y, si lo es, llama a su propiedad Count. Si IEnumerable no es una ICollection, Count () recurre a iterar la secuencia. Tal vez debería haber dicho "es muy ineficiente contar elementos usando solo IEnumerable", porque, por supuesto, la presencia de IEnumerable no le impide usar un método más eficiente si el objeto tiene uno, y los métodos de extensión LINQ to Objects lo hacen exactamente ese.
itowlson
17

IEnumerable es una interfaz que permite la iteración a través de una colección de elementos (por ejemplo, a través de la palabra clave foreach).

Una matriz es intrínseca a .NET. Contiene elementos del mismo tipo, pero de tamaño fijo. Una vez que crea una matriz con x elementos, no puede crecer ni reducirse.

IList define la interfaz para una lista y también implementa IEnumerable.

List implementa la interfaz IList; es un tipo concreto de lista.

La diferencia entre las listas de .NET y las matrices es que a las listas se les pueden agregar elementos; crecen hasta ser lo suficientemente grandes como para contener todos los elementos necesarios. La lista almacena esto internamente en una matriz y, cuando la matriz ya no es lo suficientemente grande para contener todos los elementos, se crea una nueva matriz y los elementos se copian.

IList y las matrices implementan IEnumerable. Así es como funcionan las interfaces: las clases implementan el contrato y se comportan de manera similar y, como resultado, pueden tratarse de manera similar (usted sabe que la clase implementa IEnumerable, no necesita saber los cómo ni los por qué). Le sugiero que lea sobre interfaces y demás.

Mark Simpson
fuente
1
Agregar este comentario para los lectores que no leen todas las respuestas: las matrices y List <> implementan IList <> (las matrices implementan gran parte de él de forma explícita, presumiblemente porque muchos de los miembros lanzan una NotSupportedException). También es más correcto decir que IList hereda de IEnumerable que decir que lo implementa.
phoog
9

IEnumerable e IList son interfaces . Array y List son clases. Array implementa IEnumerable. List implementa IList que extiende IEnumerable.

Editar: como mencionó itowlson en un comentario, Array también implementa IList.

jfclavette
fuente
¡Yo tampoco! Sin embargo, ¿qué sucede si comienza a llamar a IList.RemoveAt en una matriz?
Mark Simpson
Lanza una NotSupportedException. (Tenga en cuenta que IList.RemoveAt y métodos IList similares se implementan explícitamente en Array para que no aparezcan en una referencia de Array normal)
itowlson
1
@itowlson Me gustaría señalar que solo System.Array (una clase per se) implementa IList, pero T [] no implementa IList <T>. T [] implementa IEnumerable <T> en su lugar. Creo que el OP no necesariamente solicitó System.Array al pedir "Array" en mi humilde opinión
usr-local-ΕΨΗΕΛΩΝ
6

La generación de una colección IEnumerable es lenta. Ejemplo:

public IEnumerable<int> GetTwoInts()
{
  yield return 1;
  yield return 2;
}
public void Something()
{
  var twoInts = GetTwoInts();
}

En el método Something, la llamada a GetTwoInts () no dará como resultado la ejecución del método GetTwoInts, ya que la enumeración nunca se repite.

fila1
fuente
¿Podría elaborarlo un poco? No estoy seguro de haberlo entendido bien. ¿Puede mostrar una línea de código más adelante que realmente resultará en la ejecución de ese método? ¡Gracias!
paaone
@paaone hizo twoIntsuna foreachllamada o twoInts.ToList().
fila1
2

IEnumerablees una interfaz de propósito general que utilizan muchas clases, como Array, Listy Stringpara permitir que alguien itere sobre una colección. Básicamente, es lo que impulsa la foreachdeclaración.

IListsuele ser la forma en que expone las variables de tipo Lista los usuarios finales. Esta interfaz permite el acceso aleatorio a la colección subyacente.

Dmitri Nesteruk
fuente
2

Para complementar las otras respuestas, tenga en cuenta que existe una diferencia de rendimiento entre IList<T>y List<T>al ejecutar una declaración foreach.

Esto se debe a que el objeto iterador devuelto por List<T>.GetEnumeratores un tipo de valor, mientras que el devuelto por IList<T>.GetEnumeratores un tipo de referencia y, por lo tanto, requiere una asignación de memoria (consulte Enumerador del tipo de valor de la lista en c # ).

En mi opinión, IList<T>no es una interfaz muy buena de todos modos. Por ejemplo, la llamada Addcan throw (consulte ¿Por qué la matriz implementa IList? ). Si necesita encapsulación, sería mejor usar IEnumerable<T>o IReadOnlyList<T>.

ZunTzu
fuente
1

Además de otras respuestas, comprender las diferencias entre Enumerable y List / Array cuando se usa LINQ puede tener un gran impacto en el rendimiento. En resumen, Enumerable se puede percibir como un generador de consultas, mientras que List / Array es el resultado de la consulta.

En el contexto de LINQ to SQL usando EntityFramework, el primero simplemente está construyendo la consulta SQL sin ejecutarla en la base de datos ni cargar ningún dato en la memoria, mientras que el segundo es lo opuesto. Es por eso que aplazaríamos la llamada.ToList() hasta que la necesitemos en la memoria para realizar la lógica empresarial.

En otro contexto, la expresión LINQ que devuelve IEnumerable pospondrá la ejecución hasta que .ToList()se llame. Considere el ejemplo siguiente:

void Main()
{
    var w1 = "AB".AsEnumerable();
    Console.WriteLine($"W1: 1");
    w1 = w1.Where(W1);
    Console.WriteLine($"W1: 2");
    w1 = w1.Where(W2);
    Console.WriteLine($"W1: 3");
    w1.ToList();    
    Console.WriteLine($"----------");

    var w2 = "CD".AsEnumerable();
    Console.WriteLine($"W2: 1");
    w2 = w2.Where(W1);
    Console.WriteLine($"W2: 2");
    w2 = w2.ToList();
    Console.WriteLine($"W2: 3");
    w2 = w2.Where(W2);
    Console.WriteLine($"W2: 4");
    w2.ToList();    
    Console.WriteLine($"----------");
}

bool W1(char arg)
{
    Console.WriteLine($"W1:{arg}");
    return true;
}

bool W2(char arg)
{
    Console.WriteLine($"W2:{arg}");
    return true;
}

OUTPUT:
W1: 1
W1: 2
W1: 3
W1:A
W2:A
W1:B
W2:B
----------
W2: 1
W2: 2
W1:C
W1:D
W2: 3
W2: 4
W2:C
W2:D
----------

En el primer ejemplo, dos .Where()se "agregan" y se ejecutan juntos al final cuando .ToList()se llama con el elemento " A " pasando primero por la tubería y luego el elemento " B ", por lo que se ve " AABB " en la salida, mientras que en el segundo ejemplo, el .Where()se ejecuta cada vez si llamamos .ToList()inmediatamente después, por lo tanto, vemos " CD " y luego " CD " nuevamente, salida dos veces. Por lo tanto, cada vez que un Enumerable se convierte en una Lista o un Array costará una O (n) iteración sobre todos los elementos de la colección, lo que tendrá un impacto en el rendimiento cuando la colección sea grande.

Aunque no tendemos a escribir código de esta manera llamando .ToList()entre llamadas LINQ, sería más frecuente cuando factorizamos el código en métodos reutilizables que devuelven en List/Arraylugar de IEnumerable.

Sin embargo, esto no significa que siempre debamos operar IEnumerable. Como lo mencionó Towlson , operaciones tales como .Count()causarán iteración sobre la colección, mientras que List o Array tendrían esta información precalculada y, por lo tanto, la conversión List/Arraysería más eficiente si planea llamar .Count()varias veces. Esta es también la razón por la que hay una .Countpropiedad en una lista en lugar de un .Count()método para contarla.

Billy Li
fuente
0

Esta es una publicación antigua, pero aún pensé en responder. IEnumerable es un comportamiento mientras que Array es una estructura de datos (colección contigua de elementos con tamaño fijo, lo que facilita el acceso a elementos por índices). Cuando un Array implementa IEnumerable, se supone que también representa la propiedad inherente de IEnumerable (de facilitar la iteración sobre la colección).

Pramod Sharma
fuente