167 votes

Collection<T> versus List<T> : que devez-vous utiliser dans vos interfaces ?

Le code ressemble à ce qui suit :

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

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

Lorsque je lance l'analyse du code, j'obtiens la recommandation suivante.

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

Comment puis-je résoudre ce problème et quelles sont les bonnes pratiques en la matière ?

246voto

Greg Beech Points 55270

Pour répondre à la partie "pourquoi" de la question de savoir pourquoi pas List<T> Les raisons en sont la pérennité et la simplicité de l'API.

Préparer l'avenir

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 qu'il contient ne sont pas virtuelles et ne peuvent donc pas être surchargées, et qu'il n'y a pas de crochets dans sa fonction Add / Insert / Remove opérations.

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 collection que vous retournez en un type que vous pouvez sous-classer, ce qui sera un changement d'interface de rupture (bien sûr, changer la sémantique des choses comme ne pas autoriser les objets nuls peut également être un changement d'interface, mais des choses comme la mise à jour de votre état de classe interne ne le serait pas).

Ainsi, en renvoyant une classe qui peut être facilement sous-classée, telle que Collection<T> ou une interface telle que IList<T> , ICollection<T> o IEnumerable<T> vous pouvez modifier votre implémentation interne pour qu'elle soit d'un type de collection différent afin de répondre à vos besoins, sans casser le code des consommateurs car elle peut toujours être retournée comme le type qu'ils attendent.

Simplicité de l'API

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, bien que votre classe puisse avoir besoin de ces opérations en interne, il est très peu probable que les consommateurs de votre classe veuillent (ou même doivent) les appeler.

Ainsi, en proposant une classe ou une interface de collection plus simple, vous réduisez le nombre de membres que les utilisateurs de votre API voient et vous facilitez son utilisation.

1 votes

Cette réponse est tout à fait pertinente. Une autre bonne lecture sur le sujet peut être trouvée ici : web.archive.org/web/20090608080454/http://blogs.msdn.com/fxcop/

7 votes

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

0 votes

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

50voto

Jon Skeet Points 692016

Personnellement, je déclarerais qu'elle renvoie une interface plutôt qu'une collection concrète. Si vous voulez vraiment un accès par liste, utilisez IList<T> . Sinon, on considère ICollection<T> y IEnumerable<T> .

1 votes

L'IList étendrait-elle alors l'interface ICollection ?

1 votes

Oui, IList<T> étend ICollection<T>. Je vais ajouter les liens de documentation.

8 votes

@Jon : Je sais que c'est vieux, mais pouvez-vous commenter ce que Krzysztof dit à blogs.msdn.com/b/kcwalina/archive/2005/09/26/474010.aspx ? Plus précisément, son commentaire, 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. Le CA1002 semble aller dans le sens des 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.

5voto

Timothy Khouri Points 14640

Je ne pense pas que quelqu'un ait déjà répondu à la partie "pourquoi"... alors voilà. La raison pour laquelle vous "devriez" utiliser une Collection<T> au lieu d'un List<T> c'est parce que si vous exposez un List<T> alors toute personne ayant accès à votre objet peut modifier les éléments de la liste. Alors que Collection<T> est censé indiquer que vous créez vos propres méthodes "Ajouter", "Supprimer", etc.

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

Si vous avez un tableau public, ex :

public int[] MyIntegers { get; }

On pourrait penser que, comme il n'y a qu'un accesseur "get", personne ne peut modifier les valeurs, mais ce n'est pas le cas. Tout le monde peut modifier les valeurs à l'intérieur, comme ceci :

someObject.MyIngegers[3] = 12345;

Personnellement, j'utiliserais simplement 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 avez besoin de compter sur l'état des objets... alors vous voudrez faire votre propre Collection et la verrouiller à partir de là :)

0 votes

"Si vous renvoyez List<T> au code client, vous ne pourrez jamais recevoir de notifications lorsque le code client modifie la collection." - FX COP... Voir aussi " msdn.microsoft.com/fr/us/library/0fss9skc.aspx "... wow, il semble que je ne sois pas à côté de la plaque après tout :)

0 votes

Wow - des années 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é.... msdn.microsoft.com/fr/us/library/0fss9skc.aspx

2voto

chakrit Points 29562

Il s'agit surtout d'abstraire vos propres implémentations au lieu d'exposer l'objet Liste pour qu'il soit manipulé directement.

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

Collecte -> Pour une collecte 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(). Pouvez-vous nous en dire plus ?

1 votes

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

0 votes

@nawfal Et où ai-je dit que c'était le cas ?

1 votes

Vous déclarez ne pas laisser d'autres objets (ou personnes) modifier directement l'état de vos objets et citez 3 types de collections. Barring ReadOnlyCollection les deux autres n'y obéissent pas.

1voto

Harald Scheirich Points 7227

Dans ce genre de cas, j'essaie généralement d'exposer le moins d'implémentation possible. Si les consommateurs n'ont pas besoin de savoir que vous utilisez une liste, vous n'avez pas besoin de renvoyer une liste. En retournant, comme le suggère Microsoft, une collection, vous cachez le fait que vous utilisez une liste aux consommateurs de votre classe et vous les isolez d'un changement interne.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X