Pour répondre à la partie «pourquoi» de la question de savoir pourquoi pas List<T>
, les raisons sont la pérennité et la simplicité de l'API.
À l'épreuve du futur
List<T>
n'est pas conçu pour être facilement extensible en le sous-classant; il est conçu pour être rapide pour les implémentations internes. Vous remarquerez que les méthodes ne sont pas virtuelles et ne peuvent donc pas être remplacées, et qu'il n'y a pas de hooks dans ses opérations Add
/ Insert
/ Remove
.
Cela signifie que si vous avez besoin de modifier le comportement de la collection à l'avenir (par exemple pour rejeter les objets nuls que les gens essaient d'ajouter, ou pour effectuer un travail supplémentaire lorsque cela se produit, comme la mise à jour de l'état de votre classe), vous devez changer le type de la collection que vous retournez à celle que vous pouvez sous-classer, ce qui sera un changement d'interface de rupture (bien sûr, changer la sémantique de choses comme ne pas autoriser null peut également être un changement d'interface, mais des choses comme la mise à jour de l'état de votre classe interne ne le seraient pas).
Ainsi, en renvoyant soit une classe qui peut être facilement sous-classée telle que Collection<T>
ou une interface telle que IList<T>
, ICollection<T>
soit IEnumerable<T>
vous pouvez modifier votre implémentation interne pour qu'elle soit un type de collection différent pour répondre à vos besoins, sans casser le code des consommateurs car il peut toujours être retourné comme le type qu'ils attendent.
Simplicité de l'API
List<T>
contient de nombreuses opérations utiles telles que BinarySearch
, Sort
etc. Cependant, s'il s'agit d'une collection que vous exposez, il est probable que vous contrôliez la sémantique de la liste, et non les consommateurs. Ainsi, même si votre classe en interne peut avoir besoin de ces opérations, il est très peu probable que les consommateurs de votre classe veuillent (ou même devraient) les appeler.
En tant que tel, en offrant une classe ou une interface de collection plus simple, vous réduisez le nombre de membres que les utilisateurs de votre API voient et leur facilitez l'utilisation.
Je le déclarerais personnellement pour renvoyer une interface plutôt qu'une collection concrète. Si vous voulez vraiment accéder à la liste, utilisez
IList<T>
. Sinon, considérezICollection<T>
etIEnumerable<T>
.la source
We recommend using Collection<T>, ReadOnlyCollection<T>, or KeyedCollection<TKey,TItem> for outputs and properties and interfaces IEnumerable<T>, ICollection<T>, IList<T> for inputs.
CA1002, semble aller de pair avec les commentaires de Krzysztof. Je ne peux pas imaginer pourquoi une collection concrète serait recommandée au lieu d'une interface, et pourquoi la distinction entre entrées / sorties.ReadOnlyCollection<T>
n'aurait pas de sens pour une entrée. De même,IList<T>
comme le dit une entrée, "j'ai besoin de Sort () ou d'un autre membre qu'un IList a", ce qui n'a aucun sens pour une sortie. Mais ce que je voulais dire, c'est pourquoi seraitICollection<T>
recommandé comme entrée etCollection<T>
comme sortie. Pourquoi ne pas l' utiliserICollection<T>
comme sortie également comme vous l'avez suggéré?Collection<T>
et lesReadOnlyCollection<T>
deux dérivent deICollection<T>
(c'est-à-dire qu'il n'y en a pasIReadOnlyCollection<T>
). Si vous retournez l'interface, il n'est pas évident de savoir laquelle il s'agit et si elle peut être modifiée. En tout cas, merci pour ta contribution. C'était un bon test de santé mentale pour moi.Je ne pense pas que quiconque ait encore répondu à la partie «pourquoi» ... alors voilà. La raison "pour laquelle" vous "devriez" utiliser a
Collection<T>
au lieu de aList<T>
est que si vous exposez aList<T>
, toute personne ayant accès à votre objet peut modifier les éléments de la liste. Attendu qu'ilCollection<T>
est censé indiquer que vous créez vos propres méthodes "Add", "Remove", etc.Vous n'avez probablement pas à vous en soucier, car vous codez probablement l'interface pour vous-même uniquement (ou peut-être pour quelques collègues). Voici un autre exemple qui pourrait avoir du sens.
Si vous avez un tableau public, ex:
On pourrait penser que parce qu'il n'y a qu'un accesseur "get", personne ne peut jouer avec les valeurs, mais ce n'est pas vrai. Tout le monde peut changer les valeurs à l'intérieur comme ceci:
Personnellement, j'utiliserais juste
List<T>
dans la plupart des cas. Mais si vous concevez une bibliothèque de classes que vous allez donner à des développeurs aléatoires, et que vous devez vous fier à l'état des objets ... alors vous voudrez créer votre propre collection et la verrouiller à partir de là: )la source
Il s'agit principalement d'abstraire vos propres implémentations au lieu d'exposer l'objet List à manipuler directement.
Il n'est pas conseillé de laisser d'autres objets (ou personnes) modifier directement l'état de vos objets. Pensez aux getters / setters de propriété.
Collection -> Pour la collection normale
ReadOnlyCollection -> Pour les collections qui ne doivent pas être modifiées
KeyedCollection -> Lorsque vous voulez des dictionnaires à la place.
La façon de résoudre ce problème dépend de ce que vous voulez que votre classe fasse et de l'objectif de la méthode GetList (). Peux-tu élaborer?
la source
ReadOnlyCollection
les deux autres, cela ne lui obéit pas.GetList()
aider à mieux l’aider.Collection<T>
aide à résumer l'implémentation interne et empêche la manipulation directe de la liste interne. Comment?Collection<T>
est simplement un wrapper, fonctionnant sur la même instance passée. C'est une collection dynamique destinée à l'héritage, rien d'autre (la réponse de Greg est plus pertinente ici).Dans ce genre de cas, j'essaie généralement d'exposer le moins d'implémentation nécessaire. Si les consommateurs n'ont pas besoin de savoir que vous utilisez réellement une liste, vous n'avez pas besoin de renvoyer une liste. En retournant comme Microsoft suggère une collection, vous masquez le fait que vous utilisez une liste des consommateurs de votre classe et les isolez contre un changement interne.
la source
Quelque chose à ajouter même si cela fait longtemps que cela n'a pas été demandé.
Lorsque votre type de liste dérive de
List<T>
au lieu deCollection<T>
, vous ne pouvez pas implémenter les méthodes virtuelles protégées quiCollection<T>
implémentent. Cela signifie que votre type dérivé ne peut pas répondre au cas où des modifications seraient apportées à la liste. Cela est dû au fait queList<T>
vous en êtes conscient lorsque vous ajoutez ou supprimez des éléments. Être capable de répondre aux notifications est une surcharge etList<T>
ne l'offre donc pas.Dans les cas où un code externe a accès à votre collection, vous ne pouvez pas contrôler le moment où un élément est ajouté ou supprimé.
Collection<T>
Fournit donc un moyen de savoir quand votre liste a été modifiée.la source
Je ne vois aucun problème à renvoyer quelque chose comme
Si j'ai renvoyé une copie déconnectée de données internes ou un résultat détaché d'une requête de données, je peux retourner en toute sécurité
List<TItem>
sans exposer aucun détail d'implémentation et permettre d'utiliser les données renvoyées de manière pratique.Mais cela dépend du type de consommateur
IEnumerable<TItem>
auquel j'attends - si c'est quelque chose comme une grille de données, je préfère retourner, qui sera la liste des éléments copiée de toute façon dans la plupart des cas :)la source
Eh bien, la classe Collection n'est en réalité qu'une classe wrapper autour d'autres collections pour masquer leurs détails d'implémentation et d'autres fonctionnalités. Je pense que cela a quelque chose à voir avec la propriété masquant le modèle de codage dans les langages orientés objet.
Je pense que vous ne devriez pas vous en soucier, mais si vous voulez vraiment plaire à l'outil d'analyse de code, procédez comme suit:
la source
Collection<T>
ne protège ni lui-même ni la collection sous-jacente pour autant que je sache.Collection<T>
protégera immédiatement votre collection sous-jacente.