Différence entre les diverses interfaces génériques de collection en C #

11

Je joue avec C # pour Windows et le développement ASP.net MVC depuis un certain temps maintenant. Mais je ne suis toujours pas clair sur certains points. J'essaie de comprendre la différence fondamentale entre les problèmes de performances et l'utilisation et l'échange de types similaires d' interfaces de collection génériques .

Quelle est la différence fondamentale entre IEnumerable<T>, ICollection<T>, List<T>(Class)?

Je semble les utiliser et les échanger sans voir aucun problème dans mes applications. En outre, existe-t-il des collections génériques plus similaires comme celles-ci qui peuvent être échangées avec ces trois?

Pankaj Upadhyay
la source
2
il y a aussi IList <T>
jk.

Réponses:

19

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.

Jalayn
la source
Tout d'abord merci. Maintenant, le sujet de préoccupation est de savoir comment décider lequel utiliser ??. Puisque List <T> implémente les deux interfaces, pourquoi ne pas toujours l'utiliser dans tous les endroits où IEnumerable ou ICollection est requis. L'utilisation de la liste aura-t-elle toujours un impact sur les performances? Veuillez modifier votre réponse pour répondre à ces préoccupations
Pankaj Upadhyay
J'ai édité pour répondre à vos questions de performance
Jalayn
+1, mais j'ajouterais que dans les rares cas où vous avez réellement besoin de passer une liste à une méthode, vous devez utiliser IListplutôt que Listdans la déclaration de méthode.
Konamiman
@Konamiman - Cela dépend si vous avez besoin de List<>fonctionnalités spécifiques, comme .AddRange(). Le IList<>n'expose pas cela.
Bobson
6

Jetez un œil aux pages MSDN pour ICollection et IEnumerable .

En termes très abstraits, c'est ainsi que je conçois ces types.

Un IEnumerable est tout ce qui peut être énuméré - c'est-à-dire, itéré sur. Cela ne signifie pas nécessairement une «collection»; par exemple, un IQueryable implémente IEnumerable et ce n'est pas une collection, mais c'est quelque chose qui peut être interrogé pour renvoyer des objets. Pour implémenter IEnumerable, un objet doit uniquement pouvoir renvoyer un objet lorsqu'il est interrogé. Je pourrais dire que j'ai un énumérable de tâches que je vais faire aujourd'hui (ce n'est pas une liste parce que je ne l'ai pas écrite ou formulée, mais je pourrais vous dire ce que je vais faire maintenant, et si vous demandiez «et ensuite?», je serais en mesure de vous dire la tâche suivante).

Un ICollection est plus concret qu'un IEnumerable. Une différence clé est qu'une collection sait combien d'éléments elle contient; pour déterminer le nombre d'éléments dans un énumérable, vous effectuez une boucle efficace et les comptez:

Moi: Les gars, combien d'articles avez-vous chacun en vous?

Enumerable: je ne sais pas vraiment. Eh bien, voici un élément. J'en ai un autre, donc c'est deux. Et un autre, donc c'est trois ... et un autre ... ok donc c'est 2382. Plus d'articles, donc j'ai 2382. Ne me demandez pas encore parce que je devrai les parcourir tous à nouveau.

Collection: J'ai 2382 objets. Je le sais déjà.

Une collection est généralement quelque chose qui sait déjà où se trouvent tous ses éléments, et n'aura pas à aller les trouver ou les générer lorsque vous les demanderez. C'est plus ... concret qu'un énumérable.

À mon avis, la différence entre un énumérable et une collection est beaucoup plus grande que la différence entre une collection et une liste. En fait, j'ai du mal à penser à une différence pratique entre une collection et une liste, mais je pense que la liste fournit de meilleures méthodes de recherche et de commande.

Les autres types de Collection incluent Queueet Stack, ainsi que Dictionary.

Les noms de ces types sont très utiles - vous pouvez concevoir une file d'attente et une pile comme leurs homologues du monde réel, et considérer les différences que vous pourriez avoir avec une liste.

Kirk Broadhurst
la source
"J'ai du mal à penser à une différence pratique entre une collection et une liste" ... Plus tard dans votre réponse, vous y répondez vous-même. A Listest un ICollection, mais un ICollectionest pas toujours List( Queue, Stack, Dictionary, ...)
Steven Jeuris
@Steven C'est une réponse très décousue. Je suis d'accord que c'est une différence importante, mais je n'appellerais pas cela une différence pratique. Qu'est-ce que cela signifie pour l'utilisateur?
Kirk Broadhurst
C'est facile! :) Queue<int> queue = (Queue<int>)list;lancera un InvalidCastExceptionquand listn'est pas un Queue<int>. La différence entre la file d'attente et la liste est hors de portée pour cette question.
Steven Jeuris
L'aspect critique de IEnumerable n'est-il pas qu'il peut renvoyer les éléments actuels et suivants - vous faites en quelque sorte allusion à cela, mais ne le dites pas explicitement. Essentiellement, quelque chose qui implémente IEnumerable seul ne peut pas renvoyer un élément arbitraire de ses membres lorsqu'il est interrogé - uniquement l'élément actuel et le suivant.
cori
@cori C'est une façon très succincte de le dire, beaucoup plus claire que ma réponse.
Kirk Broadhurst
-1

IQueryable:

la requête n'est exécutée que pour vraiment itérer sur les éléments, peut-être en faisant un .ToList ()

IEnumerable:

liste d'éléments uniquement en avant. Vous ne pouvez pas accéder à "l'élément 4" sans passer les éléments 0-3. liste en lecture seule, vous ne pouvez pas y ajouter ou en supprimer. Peut encore utiliser l'exécution différée.

IList:

l'accès aléatoire à la liste complète entièrement en mémoire prend en charge l'ajout et la suppression

ICollection:

Est entre IEnumerable et IList. Ce qui est "le meilleur" dépend de vos besoins. Habituellement, bien qu'un IEnumerable soit "assez bon" si vous ne souhaitez afficher que des éléments. Utilisez au moins toujours la variante générique.

Zia Qammar
la source
cette réponse ne semble pas ajouter quoi que ce soit de précieux à ce qui a déjà été publié dans les réponses précédentes
gnat
c'est juste un concept simple sur tout
Zia Qammar