2008-11-07 5 views
128

Le code ressemble ci-dessous:Collection <T> par rapport à la liste <T> que devez-vous utiliser sur vos interfaces?

namespace Test 
{ 
    public interface IMyClass 
    { 
     List<IMyClass> GetList(); 
    } 

    public class MyClass : IMyClass 
    { 
     public List<IMyClass> GetList() 
     { 
      return new List<IMyClass>(); 
     } 
    } 
} 

Quand je lance l'analyse du code i obtenir la recommandation suivante.

Avertissement 3 CA1002: Microsoft.Design: Change 'List' dans 'IMyClass.GetList()' utiliser Collection, ReadOnlyCollection ou KeyedCollection

Comment dois-je résoudre ce problème et ce qui est une bonne pratique ici ?

Répondre

189

Pour répondre à la partie «pourquoi» de la question pourquoi pas List<T>, les raisons sont la pérennité et la simplicité de l'API.

avenir épreuvage

List<T> n'a pas été conçu pour être facilement extensible par le sous-classer; 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 aucun hook dans ses opérations Add/Insert/Remove. Cela signifie que si vous devez modifier le comportement de la collection à l'avenir (par exemple, pour rejeter les objets nuls que les utilisateurs tentent d'ajouter ou pour effectuer un travail supplémentaire, comme la mise à jour de votre état de classe), vous devez pour changer le type de collection que vous retournez à celui que vous pouvez sous-classer, qui sera un changement d'interface de rupture (bien sûr changer la sémantique des choses comme ne pas autoriser null peut également être un changement d'interface, mais des choses comme être).

en retournant soit une classe qui peut être facilement sous-classé tels que Collection<T> ou une interface telle que IList<T>, ICollection<T> ou IEnumerable<T> vous pouvez modifier votre implémentation interne pour être un autre type de collection pour répondre à vos besoins, sans casser le code de les consommateurs parce qu'il peut toujours être retourné comme le type qu'ils attendent.

API Simplicité

List<T> contient un grand nombre d'opérations utiles telles que BinarySearch, Sort et ainsi de suite. 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, alors que votre classe peut avoir besoin de ces opérations en interne, il est très peu probable que les consommateurs de votre classe veuillent (ou même devraient) les appeler. Ainsi, en offrant une classe ou une interface de collecte plus simple, vous réduisez le nombre de membres que les utilisateurs de votre API voient et facilitez leur utilisation.

+0

Cette réponse est morte. Une autre bonne lecture sur le sujet peut être trouvée ici: http://blogs.msdn.com/fxcop/archive/2006/04/27/faq-why-does-donotexposegenericlists-recommend-that-i-expose-collection-lt -t-gt-au lieu-de-liste-lt-t-gt-david-kean.aspx – senfo

+5

Je vois vos premiers points, mais je ne sais pas si je suis d'accord sur votre partie de simplicité API. –

+0

http://stackoverflow.com/a/398988/2632991 Voici également un très bon article sur la différence entre les collections et les listes. –

46

Je le déclarerais personnellement pour retourner une interface plutôt qu'une collection concrète. Si vous voulez vraiment l'accès à la liste, utilisez IList<T>. Autrement, considérons ICollection<T> et IEnumerable<T>.

+1

Est-ce que le IList étendrait alors l'interface ICollection? – bovium

+1

Oui, IList étend ICollection . Je vais ajouter des liens de documentation. –

+8

@Jon: Je sais que c'est vieux, mais pouvez-vous commenter ce que Krzysztof dit à http://blogs.msdn.com/b/kcwalina/archive/2005/09/26/474010.aspx? Plus précisément son commentaire, 'Nous vous recommandons d'utiliser Collection , ReadOnlyCollection ou KeyedCollection pour les sorties et les propriétés et les interfaces IEnumerable , ICollection , IList pour CA1002 inputs.' semble aller de pair avec les commentaires de Krzysztof. Je ne peux pas imaginer pourquoi une collection concrète serait recommandée à la place d'une interface, et pourquoi la distinction entre les entrées/sorties. –

-1

Bien la classe Collection est vraiment juste une classe wrapper autour d'autres collections pour cacher leurs détails d'implémentation et d'autres fonctionnalités. Je pense que cela a quelque chose à voir avec la propriété cachant le modèle de codage dans les langages orientés objet.

Je pense que vous ne devriez pas vous inquiéter, mais si vous voulez vraiment faire plaisir à l'outil d'analyse de code, il suffit de ce qui suit:

//using System.Collections.ObjectModel; 

Collection<MyClass> myCollection = new Collection<MyClass>(myList); 
+1

Cela ne fonctionnera pas en raison de l'invariance générique :( –

+1

Désolé, ce fut une faute d'orthographe.J'ai menat Collection .J'ai vraiment hâte de mettre la main sur C# 4 et la covariance générique btw! –

+0

Envelopper dans une 'Collection ' Je ne protège pas la collection sous-jacente, pour autant que je comprenne – nawfal

1

Je ne pense pas que quelqu'un a répondu « pourquoi » part encore ... alors voilà. La raison pour laquelle vous "devriez" utiliser un Collection<T> au lieu d'un List<T> est que si vous exposez un List<T>, alors quiconque ayant accès à votre objet peut modifier les éléments de la liste. Considérant que Collection<T> est censé indiquer que vous faites vos propres "Add", "Remove", etc méthodes.

Vous n'avez probablement pas besoin de vous en préoccuper, car vous êtes probablement en train de coder l'interface pour vous seul (ou peut-être quelques collègues). Voici un autre exemple qui pourrait avoir un sens.

Si vous avez un public array, ex:

public int[] MyIntegers { get; } 

On pourrait penser que parce qu'il ya seulement un accesseur « get » que personne ne peut jouer avec les valeurs, mais ce n'est pas vrai. Tout le monde peut modifier les valeurs à l'intérieur il y a comme ceci:

someObject.MyIngegers[3] = 12345; 

Personnellement, je voudrais simplement utiliser List<T> dans la plupart des cas. Mais si vous concevez une bibliothèque de classes que vous allez distribuer à des développeurs aléatoires, et que vous devez compter sur l'état des objets ... alors vous voudrez créer votre propre collection et la verrouiller à partir de là:

+2

Vous êtes assez loin de la base. http://blogs.msdn.com/fxcop/archive/2006/04/27/faq-why-does-donotexposegenericlists-recommend-that-i-expose-collection-lt-t-gt-instead-of- list-lt-t-gt-david-kean.aspx – senfo

+0

"Si vous renvoyez la liste au code client, vous ne pourrez jamais recevoir de notifications lorsque le code client modifie la collection." - FX COP ... Voir aussi "http://msdn.microsoft.com/fr-fr/library/0fss9skc.aspx" ... wow, semble que je ne suis pas hors base après tout :) –

+0

Wow - ans plus tard Je vois que le lien que j'ai collé dans le commentaire ci-dessus avait une citation à la fin, donc ça n'a pas marché ... http://msdn.microsoft.com/en-us/library/0fss9skc.aspx –

1

Il s'agit principalement d'abstraire vos propres implémentations au lieu d'exposer directement l'objet List à manipuler.

Il n'est pas recommandé de laisser d'autres objets (ou personnes) modifier directement l'état de vos objets. Pensez aux getters/setters de propriété.

Collection -> Pour la collecte normale
ReadOnlyCollection -> Pour les collections qui ne devraient pas être modifiées
KeyedCollection -> Si vous voulez dictionnaires à la place.Comment 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?

+1

Mais Collection et KeyedCollection <,> ne sont pas en lecture seule non plus. – nawfal

+0

@nawfal Et où ai-je dit que c'est? – chakrit

+1

Vous déclarez * ne pas laisser d'autres objets (ou personnes) modifier directement l'état de vos objets * et répertorier 3 types de collection. Sauf 'ReadOnlyCollection' les deux autres ne l'obéissent pas. – nawfal

1

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 retourner une liste. En revenant comme Microsoft suggère une collection, vous cachez le fait que vous utilisez une liste des consommateurs de votre classe et les isolez contre un changement interne.

0

Je ne vois pas de problème avec retour quelque chose comme

this.InternalData.Filter(crteria).ToList(); 

Si je suis retourné un déconnecté copie des données internes, ou le résultat détaché d'une requête de données - je peux retourner en toute sécurité List<TItem> sans exposer tout des détails de mise en œuvre, et permettent d'utiliser les données renvoyées de manière pratique.

Mais cela dépend de quel type de consommateur, je pense - si cela est quelque chose comme grille de données, je préfère retourner IEnumerable<TItem>qui sera la liste copié des éléments de toute façon dans la plupart des cas :)

0

Un but est de rendre le code/design plus flexible et extensible!

1

Quelque chose à ajouter si cela fait longtemps que cela a été demandé.

Lorsque votre type de liste dérive de List<T> au lieu de Collection<T>, vous ne pouvez pas implémenter les méthodes virtuelles protégées implémentées par Collection<T>. Cela signifie que votre type dérivé ne peut pas répondre si des modifications sont apportées à la liste. En effet, List<T> suppose que vous êtes averti lorsque vous ajoutez ou supprimez des éléments. Etre capable de répondre aux notifications est une surcharge et donc List<T> ne l'offre pas.

Lorsque le code externe a accès à votre collection, il se peut que vous ne contrôliez pas l'ajout ou le retrait d'un élément. Par conséquent Collection<T> fournit un moyen de savoir quand votre liste a été modifiée.

Questions connexes