207 votes

Quand utiliser IList et quand utiliser List ?

Je sais que IList est l'interface et que List est le type concret, mais je ne sais toujours pas quand utiliser chacun d'eux. Ce que je fais maintenant, c'est que si je n'ai pas besoin des méthodes Sort ou FindAll, j'utilise l'interface. Ai-je raison ? Existe-t-il un meilleur moyen de décider quand utiliser l'interface ou le type concret ?

2 votes

Si quelqu'un se pose encore la question, je trouve les meilleures réponses ici : stackoverflow.com/questions/400135/listt-or-ilistt

203voto

Lee Points 6659

Il y a deux règles que je suis :

  • Acceptez le type le plus basique qui fonctionnera
  • Retournez le type le plus riche dont votre utilisateur aura besoin

Ainsi, lorsque vous écrivez une fonction ou une méthode qui prend une collection, écrivez-la non pas pour prendre une liste, mais une IList<T>, une ICollection<T> ou une IEnumerable<T>. Les interfaces génériques fonctionneront toujours, même pour les listes hétérogènes, car System.Object peut également être un T. Cela vous épargnera des maux de tête si vous décidez d'utiliser une pile ou une autre structure de données plus tard. Si tout ce que vous avez besoin de faire dans la fonction est de la parcourir par foreach, IEnumerable<T> est vraiment tout ce que vous devriez demander.

D'un autre côté, lorsqu'on renvoie un objet à partir d'une fonction, on veut donner à l'utilisateur l'ensemble le plus riche possible d'opérations sans qu'il ait à effectuer de castings. Dans ce cas, s'il s'agit d'une List<T> interne, renvoyez une copie en tant que List<T>.

54 votes

Vous ne devriez pas traiter les types d'entrée/sortie différemment. Les types d'entrée et de sortie doivent ambos être le type le plus élémentaire (de préférence une interface) qui répondra aux besoins des clients. L'encapsulation repose sur le fait de dire aux clients le moins possible sur l'implémentation de votre classe. Si vous retournez une liste concrète, vous ne pouvez pas ensuite passer à un autre type plus performant sans obliger tous vos clients à recompiler/mettre à jour.

17 votes

Je ne suis pas d'accord avec les 2 règles... J'utiliserais le type le plus primitif et spécialement pour retourner dans ce cas IList (mieux IEnumarable) et vous devriez travailler avec List dans votre fonction interne. Ensuite, lorsque vous avez besoin de "ajouter" ou "trier", utilisez Collection, si vous avez besoin de plus, utilisez List. Ma règle d'or serait donc la suivante : Commencez toujours avec IENumarable et si vous avez besoin de plus, étendez...

3 votes

Pour votre commodité, les "deux règles" ont un nom : principe de robustesse (alias loi de Postel) .

63voto

Joe Points 60749

Les directives de Microsoft, telles que vérifiées par FxCop, découragent l'utilisation de List<T> dans les API publiques - préférez IList<T>.

Par ailleurs, je déclare désormais presque toujours les tableaux unidimensionnels comme IList<T>, ce qui me permet d'utiliser systématiquement la propriété IList<T>.Count plutôt que Array.Length. Par exemple :

public interface IMyApi
{
    IList<int> GetReadOnlyValues();
}

public class MyApiImplementation : IMyApi
{
    public IList<int> GetReadOnlyValues()
    {
        List<int> myList = new List<int>();
        ... populate list
        return myList.AsReadOnly();
    }
}
public class MyMockApiImplementationForUnitTests : IMyApi
{
    public IList<int> GetReadOnlyValues()
    {
        IList<int> testValues = new int[] { 1, 2, 3 };
        return testValues;
    }
}

4 votes

C'est l'explication / l'exemple qui me plaît le plus !

31voto

Matthew Watson Points 30804

Il y a une chose importante que les gens semblent toujours négliger :

Vous pouvez passer un tableau simple à quelque chose qui accepte un tableau de type IList<T> et ensuite vous pouvez appeler IList.Add() et recevra une exception d'exécution :

Unhandled Exception: System.NotSupportedException: Collection was of a fixed size.

Par exemple, considérez le code suivant :

private void test(IList<int> list)
{
    list.Add(1);
}

Si vous l'appelez comme suit, vous obtiendrez une exception d'exécution :

int[] array = new int[0];
test(array);

Cela se produit parce que l'utilisation de tableaux simples avec IList<T> viole le principe de substitution de Liskov.

Pour cette raison, si vous appelez IList<T>.Add() vous pouvez envisager d'exiger un List<T> au lieu d'un IList<T> .

2 votes

C'est trivialement vrai pour chaque interface. Si vous voulez aller jusqu'au bout de votre raisonnement, vous pourriez argumenter en disant qu'il ne faut jamais utiliser d'interface du tout, parce qu'une implémentation de celle-ci pourrait être rejetée. Si, d'un autre côté, vous considérez la suggestion donnée par OP de préférer List<T> sur IList<T> vous devez également être conscient des raisons pour lesquelles IList<T> est recommandé. (Par exemple blogs.msdn.microsoft.com/kcwalina/2005/09/26/ )

4 votes

@MichaWiedenmann Ma réponse est spécifique au moment où vous appelez. IList<T>.Add() . Je ne dis pas que vous ne devrait pas utiliser IList<T> - Je signale simplement un piège possible. (J'ai tendance à utiliser IEnumerable<T> o IReadOnlyList<T> o IReadOnlyCollection<T> de préférence à IList<T> si je peux.)

24voto

ICR Points 6960

Je suis d'accord avec le conseil de Lee de prendre des paramètres, mais de ne pas revenir.

Si vous spécifiez que vos méthodes renvoient une interface, cela signifie que vous êtes libre de modifier ultérieurement l'implémentation exacte sans que la méthode consommatrice ne le sache jamais. Je pensais que je n'aurais jamais besoin de changer une liste<T>, mais j'ai dû changer plus tard pour utiliser une bibliothèque de listes personnalisée pour la fonctionnalité supplémentaire qu'elle fournissait. Comme je n'avais renvoyé qu'une IList<T>, aucune des personnes qui utilisaient la bibliothèque n'a dû modifier son code.

Bien sûr, cela ne doit s'appliquer qu'aux méthodes qui sont visibles de l'extérieur (c'est-à-dire les méthodes publiques). Personnellement, j'utilise les interfaces même dans le code interne, mais comme vous êtes en mesure de modifier tout le code vous-même si vous faites des changements cassants, ce n'est pas strictement nécessaire.

9voto

tgmdbm Points 1115

Il est toujours préférable d'utiliser le type de base le plus bas possible. Cela donne à l'implémenteur de votre interface, ou au consommateur de votre méthode, la possibilité d'utiliser ce qu'il veut dans les coulisses.

Pour les collections, vous devez utiliser IEnumerable dans la mesure du possible. Cette dernière offre le plus de flexibilité mais n'est pas toujours adaptée.

1 votes

Il est toujours préférable de accepter le type de base le plus bas possible. Le retour est une autre histoire. Choisissez les options qui sont susceptibles d'être utiles. Vous pensez que votre client pourrait vouloir utiliser un accès indexé ? Empêchez-le de ToList() - votre retour IEnumerable<T> qui était déjà une liste, et retourner un IList<T> à la place. Désormais, les clients peuvent bénéficier de ce que vous pouvez leur fournir sans effort.

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