Qual è la differenza tra IEnumerable e Array, IList e List?


91

Qual è la differenza tra IEnumerablee Array?

Qual è la differenza tra IListe List?

Questi sembrano avere la stessa funzione.

Risposte:


101

IEnumerable fornisce solo funzionalità "iterabili" minime. Puoi attraversare la sequenza, ma questo è tutto. Questo ha degli svantaggi - per esempio, è molto inefficiente contare gli elementi usando IEnumerable, o per ottenere l'ennesimo elemento - ma ha anche dei vantaggi - per esempio, un IEnumerable potrebbe essere una sequenza infinita, come la sequenza dei numeri primi.

Array è una raccolta di dimensioni fisse con accesso casuale (ovvero è possibile indicizzarla).

List è una raccolta di dimensioni variabili (ovvero è possibile aggiungere e rimuovere elementi) con accesso casuale.

IList è un'interfaccia che astrae le funzionalità di elenco (conteggio, aggiunta, rimozione, accesso all'indicizzatore) dalle varie classi concrete come List, BindingList, ObservableCollection, ecc.


2
Secondo la risposta accettata qui stackoverflow.com/questions/1826658/… . Count on IEnumerable è quasi lo stesso perché cerca di chiamare Count on ICollection
Karsten

15
Sì e no. Il metodo di estensione Count () controlla se IEnumerable è anche un ICollection e, in tal caso, chiama la relativa proprietà Count. Se IEnumerable non è un ICollection, Count () ricorre all'iterazione della sequenza. Forse avrei dovuto dire "è molto inefficiente contare gli elementi usando solo IEnumerable", perché ovviamente la presenza di IEnumerable non ti impedisce di usare un metodo più efficiente se l'oggetto ne ha uno, e i metodi di estensione LINQ to Objects fanno esattamente quello.
itowlson

17

IEnumerable è un'interfaccia che consente l'iterazione attraverso una raccolta di elementi (ad esempio tramite la parola chiave foreach).

Un array è un intrinseco .NET. Contiene oggetti dello stesso tipo, ma di dimensioni fisse. Una volta creato un array con x elementi, non può crescere o ridursi.

IList definisce l'interfaccia per un elenco e implementa anche IEnumerable.

List implementa l'interfaccia IList; è un tipo concreto di lista.

La differenza tra gli elenchi .NET e gli array è che agli elenchi possono essere aggiunti elementi: crescono abbastanza da contenere tutti gli elementi richiesti. L'elenco lo memorizza internamente in un array e, quando l'array non è più abbastanza grande da contenere tutti gli elementi, viene creato un nuovo array e gli elementi vengono copiati.

IList e gli array implementano entrambi IEnumerable. È così che funzionano le interfacce: le classi implementano il contratto e si comportano in modo simile e di conseguenza possono essere trattate in modo simile (sai che la classe implementa IEnumerable, non è necessario conoscere il come o il perché). Ti suggerisco di leggere su interfacce e così via.


1
Aggiungendo questo commento per i lettori che non leggono tutte le risposte: array e List <> implementano entrambi IList <> (gli array ne implementano gran parte in modo esplicito, presumibilmente perché molti dei membri lanciano NotSupportedException). È anche più corretto dire che IList eredita da IEnumerable che dire che lo implementa.
phoog

9

IEnumerable e IList sono interfacce . Array e List sono classi. Array implementa IEnumerable. List implementa IList che estende IEnumerable.

Modifica: come itowlson ha menzionato in un commento, Array implementa anche IList.


Nemmeno io! Cosa succede se inizi a chiamare IList.RemoveAt su un array, però?
Mark Simpson

Genera un'eccezione NotSupportedException. (Nota IList.RemoveAt e metodi IList simili sono esplicitamente implementati su Array in modo che non vengano visualizzati su un normale riferimento Array!)
itowlson

1
@itowlson vorrei sottolineare che solo System.Array (una classe di per sé) implementa IList, ma T [] non implementa IList <T>. T [] implementa invece IEnumerable <T>. Penso che l'OP non abbia necessariamente chiesto System.Array quando ha chiesto "Array" IMHO
usr-local-ΕΨΗΕΛΩΝ

6

La generazione di una raccolta IEnumerable è lenta. Esempio:

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

Nel metodo Something la chiamata a GetTwoInts () non comporterà effettivamente l'esecuzione del metodo GetTwoInts poiché l'enumerazione non viene mai ripetuta.


Potresti elaborarlo un po 'per favore? Non sono sicuro di aver capito bene. Potete mostrare in seguito una riga di codice che risulterà effettivamente nell'esecuzione di quel metodo? Grazie!
paaone

@paaone ha inserito twoIntsuna foreachchiamata o twoInts.ToList().
riga1

2

IEnumerableè un'interfaccia general-purpose che viene utilizzato da molte classi, come ad esempio Array, Liste Stringal fine di lasciare che qualcuno iterare su una collezione. Fondamentalmente, è ciò che guida l' foreachaffermazione.

IListè in genere il modo in cui esponi le variabili di tipo Listagli utenti finali. Questa interfaccia consente l'accesso casuale alla raccolta sottostante.


2

Per completare le altre risposte, nota che c'è una differenza di prestazioni tra IList<T>e List<T>quando si esegue un'istruzione foreach.

Questo perché l'oggetto iteratore restituito da List<T>.GetEnumeratorè un tipo di valore mentre quello restituito da IList<T>.GetEnumeratorè un tipo di riferimento e quindi richiede un'allocazione di memoria (vedere Enumeratore del tipo di valore di elenco in c # ).

A mio parere, IList<T>non è comunque una buona interfaccia. Ad esempio, la chiamata Addpuò generare (vedere Perché array implementa IList? ). Se hai bisogno di incapsulamento, faresti meglio a usare IEnumerable<T>o IReadOnlyList<T>.


1

Oltre ad altre risposte, la comprensione della differenza tra Enumerable e List / Array quando si usa LINQ può avere un enorme impatto sulle prestazioni. In breve, Enumerable può essere percepito come un generatore di query mentre List / Array è il risultato della query.

Nel contesto di LINQ to SQL che utilizza EntityFramework, il primo si limita a creare la query SQL senza eseguirla sul database né caricare dati in memoria, mentre il secondo è l'opposto. Ecco perché rinviamo la chiamata .ToList()fino a quando non ne abbiamo bisogno nella memoria per eseguire la logica di business.

In un altro contesto, l'espressione LINQ che restituisce IEnumerable rinvia l'esecuzione fino a quando non .ToList()viene chiamata. Considera l'esempio di seguito:

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
----------

Nel primo esempio, due .Where()vengono "aggiunti" ed eseguiti insieme alla fine quando .ToList()viene chiamato con l'elemento " A " che passa prima attraverso la pipe e poi l'elemento " B ", quindi vedendo " AABB " nell'output, mentre nel secondo esempio, la .Where()viene eseguito ogni volta che se chiamiamo .ToList()subito dopo, quindi, vedere " CD " e poi " CD " ancora una volta, l'uscita per due volte. Pertanto, ogni volta che un oggetto Enumerable viene convertito in un elenco o in un array, costerà un'iterazione O (n) su tutti gli elementi della raccolta, il che avrà un impatto sulle prestazioni quando la raccolta è grande.

Sebbene non tendiamo a scrivere codice in questo modo chiamando .ToList()tra le chiamate LINQ, ciò accadrebbe più frequentemente quando fattorizziamo il codice in metodi riutilizzabili che restituiscono List/Arraypiuttosto che IEnumerable.

Questo tuttavia non significa che dovremmo sempre operare IEnumerable. Come accennato da towlson , operazioni come .Count()causeranno l'iterazione sulla raccolta mentre un elenco o un array avrebbe queste informazioni precalcolate e quindi la conversione in List/Arraysarebbe più efficiente se si prevede di chiamare .Count()più volte. Questo è anche il motivo per cui c'è una .Countproprietà in una lista piuttosto che un .Count()metodo per contarla.


0

Questo è un vecchio post, ma ho ancora pensato di rispondere. IEnumerable è un comportamento mentre Array è una struttura dati (raccolta contigua di elementi con dimensioni fisse, che facilita l'accesso agli elementi tramite indici). Quando un Array implementa IEnumerable, si suppone che rappresenti anche la proprietà intrinseca di IEnumerable (per facilitare l'iterazione sulla raccolta).

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.