List < T > è una classe e implementa sia il ICollection < T > e Interfacce IEnumerable < T > . Inoltre, ICollection < T > estende IEnumerable < T > interfaccia. Non sono intercambiabili, almeno non da tutti i punti di vista.
Se hai una lista < T & gt ;, sei sicuro che questo oggetto implementa metodi e proprietà che devono essere implementati da ICollection < T > e IEnumerable < T > interfaccia. Il compilatore lo sa e puoi lanciarli "down" su un ICollection < T > o un IEnumerable < T > implicitamente. Tuttavia, se disponi di un ICollection < T > devi prima verificare esplicitamente nel tuo codice se è un elenco < T > o qualcos'altro, forse un dizionario < T > (dove T è un KeyValuePair ) prima di inviarlo a ciò che desideri.
Sai che ICollection estende IEnumerable, quindi puoi lanciarlo su un oggetto IEnumerable. Ma se si ha solo un oggetto IEnumerable, di nuovo non si garantisce che si tratti di un elenco. Potrebbe essere, ma potrebbe essere qualcos'altro. Dovresti aspettarti un'eccezione di cast non valida se tenti di trasmettere una lista < T > a un dizionario < T > per esempio.
Quindi non sono "intercambiabili".
Inoltre, ci sono molte interfacce generiche, controlla cosa puoi trovare in System.Collections.Generic spazio dei nomi.
Modifica: per quanto riguarda il tuo commento, non c'è assolutamente alcuna penalità legata alle prestazioni se usi List < T > o una delle interfacce che implementa. Dovrai comunque creare un nuovo oggetto, controlla il seguente codice:
List<T> list = new List<T>();
ICollection<T> myColl = list;
IEnumerable<T> myEnum = list;
elenco , myColl e myEnum puntano tutti allo stesso oggetto. Sia che tu lo dichiari come Lista o ICollection o IEnumerable, sto ancora richiedendo al programma di creare una Lista. Potrei aver scritto questo:
ICollection<T> myColl = new List<T>();
myColl , in fase di runtime è ancora un elenco.
Tuttavia , questo è il punto più importante ... per ridurre l'accoppiamento e aumentare la manutenibilità devi sempre dichiarare le tue variabili e i parametri del metodo utilizzando il minimo possibile denominatore possibile per te, che sia un'interfaccia o una classe astratta o concreta.
Immagina che l'unica cosa di cui il metodo "PerformOperation" abbia bisogno è enumerare gli elementi, fare un po 'di lavoro ed uscire, in quel caso non hai bisogno di altri cento metodi disponibili in List < T & gt ;, hai solo bisogno di ciò che è disponibile in IEnumerable < T & gt ;, quindi dovrebbe essere applicato quanto segue:
public void PerformOperation(IEnumerable<T> myEnumeration) { ... }
In questo modo tu e altri sviluppatori sapete che qualsiasi oggetto di una classe che implementa IEnumerable < T > un'interfaccia può essere data a questo metodo. Può essere un elenco, un dizionario o una classe di raccolta personalizzata che un altro sviluppatore ha scritto.
Se al contrario specifichi di aver bisogno esplicitamente di una lista concreta < T > (e anche se raramente accade nella vita reale, può ancora succedere), tu e altri sviluppatori sapete che deve essere una lista o un'altra classe concreta ereditata da List.