web-dev-qa-db-fra.com

Collection <T> versus Liste <T> Que devez-vous utiliser sur vos interfaces?

Le code ressemble à celui ci-dessous:

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

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

Lorsque j'exécute l'analyse de code, j'obtiens la recommandation suivante.

Avertissement 3 CA1002: Microsoft.Design: modifier "Liste" dans "IMyClass.GetList ()" pour utiliser Collection, ReadOnlyCollection ou KeyedCollection

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

150
bovium

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.

pérennité

List<T> n'est pas conçu pour être facilement extensible en le classant; il est conçu pour être rapide pour les implémentations internes. Vous remarquerez que ses méthodes ne sont pas virtuelles et ne peuvent donc pas être remplacées. De plus, ses opérations Add/Insert/Remove ne sont pas liées.

Cela signifie que si vous devez modifier le comportement de la collection à l'avenir (par exemple, pour rejeter des objets nuls que des personnes tentent d'ajouter, ou pour effectuer un travail supplémentaire lorsque cela se produit, par exemple pour mettre à jour votre état de classe), vous devez modifier le type. de la collection vous revenez à une classe que vous pouvez sous-classer, ce qui constituera un changement d’interface révolutionnaire (bien sûr, changer la sémantique de choses telles que ne pas autoriser la valeur null peut également être un changement d’interface, mais des choses comme la mise à jour de votre état de classe interne ne le seraient pas).

Donc, en retournant soit une classe qui peut être facilement sous-classée telle que Collection<T> ou une interface telle que IList<T>, ICollection<T> ou IEnumerable<T> vous pouvez modifier votre implémentation interne afin de définir un type de collecte différent pour répondre à vos besoins, sans pour autant rompre le code des utilisateurs, car il peut toujours être renvoyé sous le type attendu.

API Simplicity

List<T> contient de nombreuses 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 interne ait 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 proposant une classe ou une interface de collecte plus simple, vous réduisez le nombre de membres que les utilisateurs de votre API voient et vous simplifiez leur utilisation.

217
Greg Beech

Je déclarerais personnellement qu'il renvoie une interface plutôt qu'une collection concrète. Si vous voulez vraiment accéder à la liste, utilisez IList<T> . Sinon, considérons ICollection<T> et IEnumerable<T> .

48
Jon Skeet

Je ne pense pas que quiconque ait répondu à la partie "pourquoi" pour l'instant ... alors voilà. La raison "pourquoi" vous "devriez" utiliser un Collection<T> au lieu d'une List<T> est parce que si vous exposez un List<T>, toute personne ayant accès à votre objet peut modifier les éléments de la liste. Tandis 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 soucier, car vous ne codez probablement que l'interface (ou peut-être quelques collègues). Voici un autre exemple qui pourrait avoir un sens.

Si vous avez un tableau public, ex:

public int[] MyIntegers { get; }

On pourrait penser que, parce qu’il n’ya 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:

someObject.MyIngegers[3] = 12345;

Personnellement, je voudrais juste 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 vous fier à l'état des objets ... vous voudrez alors créer votre propre collection et la verrouiller à partir de là: )

2
Timothy Khouri

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

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é.

Collection -> Pour collection normale
ReadOnlyCollection -> Pour les collections qui ne doivent pas être modifiées
KeyedCollection -> Quand vous voulez plutôt des dictionnaires.

Comment résoudre ce problème dépend de ce que vous voulez que votre classe fasse et du but de la méthode GetList (). Peux-tu élaborer?

1
chakrit

Dans ce type de cas, j'essaie généralement d'exposer le moins possible d'implémentation. 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 vous les isolez contre un changement interne.

1
Harald Scheirich

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 de Collection<T>, vous ne pouvez pas implémenter les méthodes virtuelles protégées que Collection<T> met en oeuvre. Cela signifie que le type dérivé ne peut pas répondre si des modifications sont apportées à la liste. Ceci est dû au fait List<T> _ suppose que vous êtes conscient 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.

Dans les cas où le code externe a accès à votre collection, vous ne pouvez pas contrôler le moment où un élément est ajouté ou supprimé. Donc Collection<T> permet de savoir quand votre liste a été modifiée.

1
NullReference

Je ne vois aucun problème à retourner quelque chose comme

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

Si je retourne un copie de données interne déconnecté ou un résultat détaché d'une requête de données - je peux retourner en toute sécurité List<TItem> sans exposer les détails de l’implémentation et permettre d’utiliser les données renvoyées de manière pratique.

Mais cela dépend du type de consommateur auquel je m'attend - s'il s'agit d'une sorte de grille de données que je préfère renvoyer IEnumerable<TItem>qui sera la liste des éléments copiés de toute façon dans la plupart des cas :)

0
Konstantin Isaev