Question List ou IList


Quelqu'un peut-il m'expliquer pourquoi je voudrais utiliser IList sur List en C #?

Question connexe: Pourquoi est-il considéré comme mauvais d'exposer List<T>


428
2017-12-30 12:18


origine


Réponses:


Si vous exposez votre classe à travers une bibliothèque que d'autres vont utiliser, vous souhaitez généralement l'exposer via des interfaces plutôt que des implémentations concrètes. Cela vous aidera si vous décidez de modifier l'implémentation de votre classe ultérieurement pour utiliser une autre classe concrète. Dans ce cas, les utilisateurs de votre bibliothèque n'auront pas besoin de mettre à jour leur code puisque l'interface ne change pas.

Si vous l'utilisez simplement en interne, vous ne vous en souciez peut-être pas autant, et en utilisant List<T> peut-être OK.


383
2017-12-30 12:23



La réponse la moins populaire est que les programmeurs aiment prétendre que leur logiciel va être réutilisé dans le monde entier, alors que la majorité des projets seront maintenus par un petit nombre de personnes et que de bonnes phrases sonores liées à l’interface sont toi même.

Astronautes d'architecture. Les chances d'écrire votre propre IList qui ajoute quelque chose à celles déjà présentes dans le framework .NET sont si éloignées qu'il est théoriquement réservé aux "meilleures pratiques".

Software astronauts

Évidemment, si on vous demande si vous utilisez lors d'une interview, vous dites IList, souriez, et tous les deux ont l'air ravis d'être si intelligents. Ou pour une API publique, IList. J'espère que vous obtenez mon point.


265
2017-10-05 22:53



L'interface est une promesse (ou un contrat).

Comme toujours avec les promesses - plus petit mieux c'est.


182
2017-12-30 18:22



Certaines personnes disent "toujours utiliser IList<T> au lieu de List<T>".
Ils veulent que vous changiez vos signatures de méthode void Foo(List<T> input) à void Foo(IList<T> input).

Ces personnes ont tort.

C'est plus nuancé que ça. Si vous retournez un IList<T> Dans le cadre de l'interface publique de votre bibliothèque, vous laissez des options intéressantes pour éventuellement faire une liste personnalisée à l'avenir. Vous n'avez peut-être jamais besoin de cette option, mais c'est un argument. Je pense que c'est l'argument entier pour renvoyer l'interface au lieu du type concret. Il vaut la peine de mentionner, mais dans ce cas, il a un sérieux défaut.

En tant que contre-argument mineur, vous pouvez trouver chaque appelant a besoin d'un List<T> de toute façon, et le code d'appel est jonché de .ToList()

Mais plus important encore, Si vous acceptez un IList comme paramètre, il vaut mieux faire attention, car IList<T> et List<T> ne vous comportez pas de la même façon. Malgré la similitude de nom, et malgré le partage d'un interface ils font ne pas exposer le même Contrat.

Supposons que vous ayez cette méthode:

public Foo(List<int> a)
{
    a.Add(someNumber);
}

Un collègue utile "refactors" la méthode pour accepter IList<int>.

Votre code est maintenant cassé, car int[] met en oeuvre IList<int>, mais est de taille fixe. Le contrat pour ICollection<T> (la base de IList<T>) nécessite le code qui l’utilise pour vérifier le IsReadOnly Indicateur avant d'essayer d'ajouter ou de supprimer des éléments de la collection. Le contrat pour List<T> ne fait pas.

Le principe de substitution de Liskov (simplifié) stipule qu’un type dérivé devrait pouvoir être utilisé à la place d’un type de base, sans conditions préalables ou conditions supplémentaires.

Cela donne l'impression de ne pas respecter le principe de substitution de Liskov.

 int[] array = new[] {1, 2, 3};
 IList<int> ilist = array;

 ilist.Add(4); // throws System.NotSupportedException
 ilist.Insert(0, 0); // throws System.NotSupportedException
 ilist.Remove(3); // throws System.NotSupportedException
 ilist.RemoveAt(0); // throws System.NotSupportedException

Mais ce n'est pas le cas. La réponse à cela est que l'exemple utilisé IList <T> / ICollection <T> est incorrect. Si vous utilisez un ICollection <T>, vous devez vérifier le drapeau IsReadOnly.

if (!ilist.IsReadOnly)
{
   ilist.Add(4);
   ilist.Insert(0, 0); 
   ilist.Remove(3);
   ilist.RemoveAt(0);
}
else
{
   // what were you planning to do if you were given a read only list anyway?
}

Si quelqu'un vous passe un tableau ou une liste, votre code fonctionnera correctement si vous vérifiez le drapeau à chaque fois et que vous avez un repli ... Mais vraiment; qui fait ça? Ne savez-vous pas à l'avance si votre méthode a besoin d'une liste pouvant contenir des membres supplémentaires? ne spécifiez-vous pas cela dans la signature de la méthode? Que feriez-vous exactement si vous aviez une liste de lecture seule comme int[]?

Vous pouvez remplacer un List<T> en code qui utilise IList<T>/ICollection<T>  correctement. Toi ne peux pas garantir que vous pouvez remplacer un IList<T>/ICollection<T> en code qui utilise List<T>.

Il y a un appel au Principe de Responsabilité Unique / Principe de Ségrégation d'Interface dans beaucoup d'arguments pour utiliser des abstractions au lieu de types concrets - dépendent de l'interface la plus étroite possible. Dans la plupart des cas, si vous utilisez un List<T> et vous pensez que vous pourriez utiliser une interface plus étroite à la place - pourquoi ne pas IEnumerable<T>? C'est souvent un meilleur ajustement si vous n'avez pas besoin d'ajouter des éléments. Si vous devez ajouter à la collection, utilisez le type de béton, List<T>.

Pour moi IList<T> (et ICollection<T>) est la pire partie du framework .NET. IsReadOnly viole le principe de la moindre surprise. Une classe, telle que Array, qui ne permet jamais d’ajouter, d’insérer ou de supprimer des éléments, ne doit pas implémenter une interface avec les méthodes Ajouter, Insérer et Supprimer. (voir également https://softwareengineering.stackexchange.com/questions/306105/implementing-an-interface-when-you-dont-need-one-of-the-properties)

Est IList<T> un bon ajustement pour votre organisation? Si un collègue vous demande de changer une signature de méthode à utiliser IList<T> au lieu de List<T>, demandez-leur comment ils ont ajouté un élément à un IList<T>. S'ils ne connaissent pas IsReadOnly (et la plupart des gens ne le font pas), alors n'utilisez pas IList<T>. Déjà.


Notez que l'indicateur IsReadOnly provient d'ICollection <T> et indique si des éléments peuvent être ajoutés ou supprimés de la collection. mais juste pour vraiment confondre les choses, cela n'indique pas si elles peuvent être remplacées, ce qui peut être dans le cas des tableaux (qui retournent IsReadOnlys == true).

Pour plus d'informations sur IsReadOnly, voir définition msdn de ICollection <T> .IsReadOnly


44
2018-04-12 10:17



List<T> est une implémentation spécifique de IList<T>, qui est un conteneur qui peut être adressé de la même manière qu'un tableau linéaire T[] en utilisant un index entier. Lorsque vous spécifiez IList<T> En tant que type d'argument de la méthode, vous spécifiez uniquement que vous avez besoin de certaines fonctionnalités du conteneur.

Par exemple, la spécification d'interface n'applique pas une structure de données spécifique à utiliser. L'implémentation de List<T> arrive à la même performance pour accéder, supprimer et ajouter des éléments en tant que tableau linéaire. Cependant, vous pouvez imaginer une implémentation soutenue par une liste chaînée, pour laquelle l'ajout d'éléments à la fin est moins cher (temps constant) mais l'accès aléatoire est beaucoup plus coûteux. (Notez que .NET LinkedList<T>Est-ce que ne pas mettre en place IList<T>.)

Cet exemple vous indique également qu'il peut y avoir des situations où vous devez spécifier l'implémentation, pas l'interface, dans la liste d'arguments: Dans cet exemple, chaque fois que vous avez besoin d'une caractéristique de performance d'accès particulière. Ceci est généralement garanti pour une implémentation spécifique d'un conteneur (List<T> documentation: "Il met en œuvre IList<T> interface générique utilisant un tableau dont la taille est augmentée dynamiquement selon les besoins. ").

En outre, vous pouvez envisager d'exposer la fonctionnalité minimale dont vous avez besoin. Par exemple. si vous n'avez pas besoin de changer le contenu de la liste, vous devriez probablement envisager d'utiliser IEnumerable<T>, lequel IList<T> s'étend.


36
2017-12-30 14:56



Je tournerais la question un peu, au lieu de justifier pourquoi vous devriez utiliser l'interface plutôt que l'implémentation concrète, essayez de justifier pourquoi vous utiliseriez l'implémentation concrète plutôt que l'interface. Si vous ne pouvez pas le justifier, utilisez l'interface.


27
2017-09-08 21:47



IList <T> est une interface afin que vous puissiez hériter d'une autre classe et implémenter toujours IList <T> alors que l'héritage de List <T> vous empêche de le faire.

Par exemple, s'il existe une classe A et que votre classe B en hérite, vous ne pouvez pas utiliser List <T>

class A : B, IList<T> { ... }

17
2017-12-30 12:23