80 votes

Est de retour IList<T> pire que de retourner T[] ou de la Liste<T>?

Les réponses à des questions de ce genre: List<T> ou IList<T> toujours semblent d'accord pour dire que le retour d'une interface est mieux que de retourner une mise en œuvre concrète d'une collection. Mais j'ai du mal avec cette. L'instanciation d'une interface est impossible, donc si votre méthode est de retour en une interface, et bien c'est encore de retour d'une mise en œuvre spécifique. J'ai essayé un peu avec cela par écrit 2 petites méthodes:

public static IList<int> ExposeArrayIList()
{
    return new[] { 1, 2, 3 };
}

public static IList<int> ExposeListIList()
{
    return new List<int> { 1, 2, 3 };
}

Et de les utiliser dans mon programme de test:

static void Main(string[] args)
{
    IList<int> arrayIList = ExposeArrayIList();
    IList<int> listIList = ExposeListIList();

    //Will give a runtime error
    arrayIList.Add(10);
    //Runs perfectly
    listIList.Add(10);
}

Dans les deux cas, lorsque j'essaie d'ajouter une nouvelle valeur, mon compilateur me donne pas d'erreur, mais, évidemment, la méthode qui expose mon tableau en tant qu' IList<T> donne une erreur d'exécution lorsque j'essaie d'ajouter quelque chose à cela. Donc, les gens qui ne savent pas ce qui se passe dans ma méthode, et ont pour ajouter des valeurs, sont obligés de la première copie de mon IList d'un List à être en mesure d'ajouter des valeurs sans risquer des erreurs. Bien sûr, ils peuvent faire un typecheck pour voir si ils sont traiter avec un List ou Array, mais si ils ne le font pas, et ils veulent ajouter des éléments à la collection qu'ils ont pas d'autre choix que de copier l' IList d'un List, même si elle ne l'est déjà un List. Si un tableau ne jamais être exposés en IList?

Une autre source de préoccupation est fondée sur la a accepté la réponse de la question liée (l'emphase est mienne):

Si vous êtes exposant votre classe grâce à une bibliothèque que d'autres l'utilisent, en général, vous souhaitez exposer via des interfaces plutôt que des implémentations concrètes. Cela aidera si vous décidez de changer la mise en œuvre de votre classe, plus tard, pour utiliser une autre classe de béton. Dans ce cas, les utilisateurs de votre bibliothèque n'aurez pas besoin de mettre à jour leur code depuis l'interface ne change pas.

Si vous êtes juste en utilisant en interne, vous ne se soucient pas tellement, et en utilisant la Liste peut être ok.

Imaginez quelqu'un réellement utilisé mon IList<T> qu'ils ont obtenu de mon ExposeListIlist() méthode juste comme ça pour ajouter/supprimer des valeurs. Tout fonctionne très bien. Mais maintenant, comme la réponse suggère, parce que le renvoi d'une interface est plus flexible que je retourne un tableau au lieu d'une Liste (pas de problème de mon côté!), alors ils sont un régal...

TLDR:

1) Exposer une interface causes inutiles jette? N'est-ce pas important?

2) Parfois, si les utilisateurs de la bibliothèque ne pas utiliser un cast, leur code peut briser lorsque vous changez votre méthode, même si la méthode reste tout à fait correct.

Je suis probablement avoir à y penser, mais je n'ai pas le consensus général que la restitution d'une interface est préférable à un retour de mise en œuvre.

107voto

Dennis Points 14573

C'est peut-être pas répondre directement à votre question, mais en .NET 4.5+, je préfère suivre ces règles lors de la conception de public ou protégé d'Api:

  • faire revenir IEnumerable<T>, si seulement l'énumération est disponible;
  • faire revenir IReadOnlyCollection<T> si l'énumération et les éléments de comptage sont disponibles;
  • faire revenir IReadOnlyList<T>, si l'énumération des éléments de comptage et un accès indexé sont disponibles;
  • faire revenir ICollection<T> si l'énumération des éléments de comptage et de modification sont disponibles;
  • faire revenir IList<T>, si l'énumération des éléments de comptage, indexés d'accès et de modification sont disponibles.

Deux dernières options supposer, que la méthode ne doit pas retourner tableau en IList<T> mise en œuvre.

31voto

dotctor Points 8003

Non, parce que le consommateur doit savoir ce qu'est exactement IList est:

IList est un descendant de l'interface ICollection et est la base l'interface de tous les non-générique listes. IList implémentations de tomber dans trois catégories: en lecture seule, de taille fixe, et de taille variable. Un lecture seule IList ne peut pas être modifié. Une taille fixe IList ne permet pas l'ajout ou la suppression d'éléments, mais il permet la modification de les éléments existants. Une taille variable, IList permet l'ajout, la suppression, et la modification d'éléments.

Vous pouvez vérifier l' IList.IsFixedSize et IList.IsReadOnly et faire ce que vous voulez avec elle.

Je pense que IList est un exemple de la graisse de l'interface et il doit avoir été divisés en plusieurs petites interfaces et il viole aussi le principe de substitution de Liskov lorsque vous revenez d'un tableau comme un IList.

Lire plus si vous voulez prendre une décision sur le retour de l'interface


Mise à JOUR

Creuser plus et j'ai trouvé qu' IList<T> ne met pas en oeuvre IList et IsReadOnly est accessible par le biais de l'interface de base ICollection<T> , mais il n'y a pas d' IsFixedSize pour IList<T>.

12voto

CodeCaster Points 38181

Comme avec tous les "interface de rapport de mise en œuvre", que vous avez à réaliser ce que d'exposer un membre du public moyen: il définit l'API publique de cette classe.

Si vous exposez un List<T> en tant que membre (terrain, propriété, méthode, ...), vous dites à la consommation de ce membre: le type obtenu par l'accès à cette méthode est un List<T>, ou quelque chose de dérivé.

Maintenant, si vous exposer une interface, vous masquez le "détail de l'implémentation" de votre classe en utilisant un type de béton. Bien sûr, vous ne pouvez pas instancier IList<T>, mais vous pouvez utiliser un Collection<T>, List<T>, dérivations de celle-ci ou de votre propre type de mise en œuvre de IList<T>.

La réelle question est "Pourquoi est - Array œuvre IList<T>", ou "Pourquoi l' IList<T> interface de sorte que beaucoup de membres".

Il dépend aussi de ce que vous voulez que les consommateurs de ce membre. Si vous avez réellement de retour d'un membre interne par le biais de votre Expose... membre, vous aurez envie de revenir un new List<T>(internalMember) de toute façon, sinon les consommateurs peuvent essayer et de les jeter à l' IList<T> et de modifier vos membres internes.

Si vous venez juste d'attendre des consommateurs pour itérer les résultats, exposer IEnumerable<T> ou IReadOnlyCollection<T> à la place.

8voto

craftworkgames Points 2919

Soyez prudent avec couverture citations qui sont prises hors de leur contexte.

De retour d'une interface est mieux que de retourner une mise en œuvre concrète

Cette citation n'a de sens que si elle est utilisée dans le contexte des principes SOLIDES. Il y a 5 principes, mais pour les fins de cette discussion, nous allons simplement parler de la dernière 3.

Inversion de dépendance principe

"Dépendent des Abstractions. Ne dépendent pas de concrétions."

À mon avis, ce principe est le plus difficile à comprendre. Mais si vous regardez la citation soigneusement qu'il ressemble beaucoup à l'original de votre soumission.

Dépendent des interfaces (abstractions). Ne pas dépendre des implémentations concrètes (concrétions).

C'est toujours un peu déroutant, mais si l'on commence à appliquer les autres principes, tout ça commence à faire beaucoup plus de sens.

Principe de substitution de Liskov

"des objets dans un programme doit être remplaçable avec les instances de leurs sous-types, sans altérer la justesse de ce programme."

Comme vous l'avez souligné, au retour d'une Array est clairement un comportement différent au renvoi de la List<T> , même si elles mettent en œuvre IList<T>. C'est certainement l'une violation de LSP.

La chose importante à réaliser est que les interfaces sont sur le consommateur. Si vous êtes de retour d'une interface, vous avez créé un contrat que les méthodes ou propriétés de l'interface peuvent être utilisés sans changer le comportement du programme.

L'Interface de la ségrégation principe

"de nombreux clients des interfaces spécifiques sont meilleurs que d'un usage général de l'interface."

Si vous êtes de retour d'une interface, vous devez retourner la plupart des clients spécifiques de l'interface de votre application prend en charge. En d'autres termes, si vous n'êtes pas attendre le client pour appeler l' Add méthode, vous ne devriez pas retourner une interface avec un Add méthode.

Malheureusement, les interfaces de la .NET framework (en particulier les premières versions) ne sont pas toujours idéales interfaces spécifiques du client. Bien que, comme @Dennis a souligné, dans sa réponse, il y a beaucoup plus de choix .NET 4.5+.

4voto

Martin Maat Points 399

Au retour d'une interface n'est pas nécessairement mieux que de retourner une mise en œuvre concrète d'une collection. Vous devriez toujours avoir une bonne raison pour utiliser une interface au lieu d'un type concret. Dans votre exemple, il semble inutile de le faire.

Des raisons valables d'utiliser une interface pourrait être:

  1. Vous ne savez pas ce que la mise en œuvre des méthodes de retourner à l'interface ressemble et il y a peut-être plusieurs, développés au fil du temps. Il peut y avoir d'autres personnes de les écrire, d'autres sociétés. Donc, vous voulez juste de s'entendre sur le minimum vital et le laisser jusqu'à eux la façon de mettre en œuvre la fonctionnalité.

  2. Vous souhaitez exposer certaines des fonctionnalités communes indépendant de votre hiérarchie de classes dans un type de façon sécuritaire. Les objets de différents types de base que devraient offrir les mêmes méthodes serait de mettre en œuvre votre interface.

On pourrait dire que 1 et 2 sont fondamentalement la même raison. Ils sont deux différents scénarios qui conduisent finalement à la même nécessité.

"C'est un contrat". Si le contrat est conclu avec vous-même et votre application est fermée à la fois la fonctionnalité et le temps, il n'y a souvent pas de point à l'aide d'une interface.

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