List <T> est une classe et implémente les interfaces ICollection <T> et IEnumerable <T> . De plus, ICollection <T> étend l'interface IEnumerable <T>. Ils ne sont pas interchangeables, du moins pas à tous points de vue.
Si vous avez un List <T>, vous êtes assuré que cet objet implémente les méthodes et les propriétés requises pour être implémentées par l'interface ICollection <T> et IEnumerable <T>. Le compilateur le sait et vous êtes autorisé à les convertir "vers le bas" en ICollection <T> ou IEnumerable <T> implicitement. Cependant, si vous avez un ICollection <T>, vous devez d'abord vérifier explicitement dans votre code s'il s'agit d'un List <T> ou autre chose, peut-être un Dictionary <T> (où T est un KeyValuePair ) avant de le convertir en ce que vous le désir.
Vous savez que ICollection étend IEnumerable, vous pouvez donc le convertir en un IEnumerable. Mais si vous n'avez qu'un IEnumerable, encore une fois, vous n'êtes pas assuré qu'il s'agit d'une liste. C'est possible, mais ça pourrait être autre chose. Vous devez vous attendre à une exception de transtypage non valide si vous tentez de transtyper une liste <T> en un dictionnaire <T> par exemple.
Ils ne sont donc pas "interchangeables".
En outre, il existe de nombreuses interfaces génériques, vérifiez ce que vous pouvez trouver dans l' espace de noms System.Collections.Generic .
Edit: concernant votre commentaire, il n'y a absolument aucune pénalité de performance si vous utilisez List <T> ou l'une des interfaces qu'il implémente. Vous devrez toujours créer un nouvel objet, consultez le code suivant:
List<T> list = new List<T>();
ICollection<T> myColl = list;
IEnumerable<T> myEnum = list;
list , myColl et myEnum pointent tous vers le même objet. Que vous le déclariez comme une liste ou une ICollection ou un IEnumerable, je demande toujours au programme de créer une liste. J'aurais pu écrire ceci:
ICollection<T> myColl = new List<T>();
myColl , lors de l'exécution est toujours une liste.
Cependant , c'est le point le plus important ... pour réduire le couplage et augmenter la maintenabilité, vous devez toujours déclarer vos variables et paramètres de méthode en utilisant le plus petit dénominateur possible pour vous, qu'il s'agisse d'une interface ou d'une classe abstraite ou concrète.
Imaginez que la seule chose dont la méthode "PerformOperation" ait besoin est d'énumérer les éléments, de faire un peu de travail et de quitter, dans ce cas vous n'avez pas besoin des centaines de méthodes supplémentaires disponibles dans List <T>, vous avez seulement besoin de ce qui est disponible dans IEnumerable <T >, les règles suivantes devraient donc s'appliquer:
public void PerformOperation(IEnumerable<T> myEnumeration) { ... }
Ce faisant, vous et les autres développeurs savez que tout objet d'une classe implémentant l'interface IEnumerable <T> peut être attribué à cette méthode. Il peut s'agir d'une liste, d'un dictionnaire ou d'une classe de collection personnalisée écrite par un autre développeur.
Si au contraire vous spécifiez que vous avez explicitement besoin d'une liste concrète <T> (et bien que ce soit rarement le cas dans la vie réelle, cela peut toujours arriver), vous et les autres développeurs savez qu'il doit s'agir d'une liste ou d'une autre classe concrète héritant de la liste.