Quelqu'un peut-il m'expliquer pourquoi je voudrais utiliser IList plutôt que List in C #?
Question connexe: Pourquoi est-il jugé mauvais d’exposer List<T>
Si vous exposez votre classe via une bibliothèque que d'autres utiliseront, 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 ultérieurement l'implémentation de votre classe pour utiliser une classe concrète différente. Dans ce cas, les utilisateurs de votre bibliothèque n'auront pas besoin de mettre à jour leur code car l'interface ne change pas.
Si vous l'utilisez uniquement en interne, vous ne vous en soucierez peut-être pas trop, et utiliser List<T>
peut être acceptable.
La réponse moins populaire est que les programmeurs prétendent que leurs logiciels vont être réutilisés dans le monde entier. En réalité, la plupart des projets seront gérés par un petit nombre de personnes. toi même.
Astronautes Architecture . Les chances que vous écriviez votre propre IList qui ajoute quoi que ce soit à celles déjà présentes dans le framework .NET sont tellement lointaines que ce sont des jelly tots théoriques réservées aux "meilleures pratiques".
Évidemment, si l’on vous demande ce que vous utilisez dans une interview, vous dites IListe, souriez, et vous avez l’air enchanté de vous être si malins. Ou pour une API publique, IList. J'espère que vous obtenez mon point.
L'interface est une promesse (ou un contrat).
Comme toujours avec les promesses - plus petit, mieux c'est .
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[]
à l'aide d'un index entier. Lorsque vous spécifiez IList<T>
comme type d'argument de la méthode, vous indiquez uniquement que vous avez besoin de certaines fonctionnalités du conteneur.
Par exemple, la spécification d'interface n'impose pas de structure de données spécifique à utiliser. L'implémentation de List<T>
a les mêmes performances pour accéder, supprimer et ajouter des éléments en tant que tableau linéaire. Cependant, vous pouvez imaginer une implémentation reposant sur une liste chaînée, pour laquelle l'ajout d'éléments à la fin est moins coûteux (temps constant) mais l'accès aléatoire beaucoup plus coûteux. (Notez que .NET LinkedList<T>
fait pas implémente IList<T>
.)
Cet exemple vous indique également qu'il peut arriver que vous deviez spécifier l'implémentation, et non l'interface, dans la liste des arguments: Dans cet exemple, 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 (documentation List<T>
: "Elle implémente l'interface générique IList<T>
à l'aide d'un tableau dont la taille est augmentée dynamiquement selon les besoins.").
En outre, vous pouvez envisager d'exposer le moins de fonctionnalités 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.
Je voudrais inverser un peu la question, au lieu de justifier pourquoi vous devriez utiliser l'interface par rapport à 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.
IList <T> est une interface pour que vous puissiez hériter d'une autre classe tout en implémentant IList <T> tout en héritant de List <T>.
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> { ... }
Un principe de TDD et OOP est généralement la programmation d’une interface et non d’une implémentation.
Dans ce cas particulier, étant donné que vous parlez essentiellement d'une construction de langage, non d'une personnalisation, cela n'aura généralement aucune importance, mais supposons par exemple que vous avez trouvé que List ne prenait pas en charge quelque chose dont vous aviez besoin. Si vous aviez utilisé IList dans le reste de l'application, vous pouvez étendre List à votre propre classe personnalisée tout en conservant la possibilité de la transmettre sans refactoring.
Le coût pour le faire est minime, pourquoi ne pas vous épargner le mal de tête plus tard? C'est le principe de l'interface.
public void Foo(IList<Bar> list)
{
// Do Something with the list here.
}
Dans ce cas, vous pouvez passer dans n'importe quelle classe implémentant l'interface IList <Bar>. Si vous utilisiez plutôt List <Bar>, seule une instance List <Bar> pourrait être transmise.
La méthode IList <Bar> est plus faiblement couplée que la méthode List <Bar>.
Supposant qu'aucune de ces questions (ou réponses) de List vs IList ne mentionne la différence de signature. (C'est pourquoi j'ai cherché cette question sur SO!)
Voici donc les méthodes contenues par List qui ne se trouvent pas dans IList, du moins à partir de .NET 4.5 (circa 2015)
Le cas le plus important pour l'utilisation d'interfaces sur des implémentations concerne les paramètres de votre API. Si votre API utilise un paramètre List, toute personne l'utilisant doit utiliser List. Si le type de paramètre est IList, l'appelant dispose de beaucoup plus de liberté et peut utiliser des classes dont vous n'avez jamais entendu parler, qui n'existaient peut-être même pas lorsque votre code a été écrit.
Que se passe-t-il si .NET 5.0 remplace System.Collections.Generic.List<T>
par System.Collection.Generics.LinearList<T>
? .NET a toujours le nom List<T>
mais garantit que IList<T>
est un contrat. Donc, à mon humble avis, nous (au moins je) n’est pas censé utiliser le nom de quelqu'un (bien que ce soit .NET dans ce cas) et avoir des ennuis plus tard.
En cas d'utilisation de IList<T>
, l'appelant est toujours assuré de fonctionner, et l'implémenteur est libre de modifier la collection sous-jacente en une autre implémentation concrète alternative de IList
Tous les concepts sont essentiellement énoncés dans la plupart des réponses ci-dessus concernant l'utilisation de l'interface sur des implémentations concrètes.
IList<T> defines those methods (not including extension methods)
IList<T>
lien MSDN
List<T>
implémente ces neuf méthodes (sans compter les méthodes d'extension), en plus de cela, il a environ 41 méthodes publiques, ce qui pèse dans votre considération sur celle à utiliser dans votre application.
List<T>
lien MSDN
Vous le feriez parce que définir un IList ou un ICollection s’ouvrirait à d’autres implémentations de vos interfaces.
Vous voudrez peut-être avoir un IOrderRepository qui définit une collection de commandes dans un IList ou ICollection. Vous pourriez alors avoir différents types d’implémentations pour fournir une liste d’ordres tant qu’ils sont conformes aux "règles" définies par votre IList ou ICollection.
IList <> est presque toujours préférable, contrairement aux conseils de l’autre auteur. Toutefois, notez il existe un bogue dans .NET 3.5 sp 1 lorsqu’un IList <> exécute plusieurs cycles de sérialisation/désérialisation avec WCF DataContractSerializer.
Il existe maintenant un SP pour résoudre ce problème: KB 971030
L’interface permet d’obtenir au moins les méthodes attendues; être conscient de la définition de l'interface, à savoir. toutes les méthodes abstraites qui doivent être implémentées par toute classe héritant de l'interface. Ainsi, si un utilisateur possède une grande classe avec plusieurs méthodes autres que celles qu’il a héritées de l’interface pour une fonctionnalité supplémentaire, celles-ci ne vous étant d'aucune utilité, il est préférable d'utiliser une référence à une sous-classe (dans ce cas, le paramètre interface) et lui assigner l’objet de classe concret.
l’avantage supplémentaire est que votre code est à l’abri de tout changement de classe concrète puisque vous ne vous abonnez qu’à quelques-unes des méthodes de classe concrète. Celles-ci sont celles qui seront présentes tant que la classe concrète hérite de l’interface que vous êtes. en utilisant. sa sécurité pour vous et sa liberté pour le codeur qui écrit une implémentation concrète pour modifier ou ajouter plus de fonctionnalités à sa classe concrète.
Vous pouvez examiner cet argument sous plusieurs angles, y compris celui d'une approche purement OO qui consiste à programmer sur une interface et non sur une implémentation. Avec cette idée, utiliser IList suit le même principe que de faire circuler et utiliser des interfaces que vous définissez à partir de zéro. Je crois également aux facteurs d'évolutivité et de flexibilité fournis par une interface en général. Si une classe implémentant IList <T> doit être étendue ou modifiée, le code consommateur ne doit pas être modifié; il sait ce que le contrat d'interface IList respecte. Cependant, l'utilisation d'une implémentation concrète et d'une liste <T> sur une classe en cours de modification pourrait entraîner une modification du code appelant. En effet, une classe adhérant à IList <T> garantit un certain comportement qui n'est pas garanti par un type concret utilisant List <T>.
Avoir également le pouvoir de faire quelque chose comme modifier l'implémentation par défaut de List <T> sur une classe implémentant IList <T> pour, par exemple, la méthode .Add, .Remove ou toute autre méthode IList, donne au développeur un lot de flexibilité et puissance, sinon prédéfini par Liste <T>
En règle générale, une bonne approche consiste à utiliser IList dans votre API publique (le cas échéant, une sémantique de liste est nécessaire), puis à Répertorier en interne pour implémenter l'API. Cela vous permet de changer d’implémentation d’IList sans casser le code qui utilise votre classe.
Le nom de classe List peut être modifié dans la prochaine structure .net, mais l'interface ne changera jamais, car interface est contractuelle.
Notez que si votre API ne doit être utilisée que dans des boucles foreach, etc., vous pouvez envisager d'exposer simplement IEnumerable.