web-dev-qa-db-fra.com

Dois-je toujours retourner IEnumerable <T> au lieu de IList <T>?

Lorsque j'écris mon DAL ou un autre code qui renvoie un ensemble d'articles, dois-je toujours faire ma déclaration de retour:

public IEnumerable<FooBar> GetRecentItems()

ou

public IList<FooBar> GetRecentItems()

Actuellement, dans mon code, j'ai essayé d'utiliser IEnumerable autant que possible, mais je ne sais pas si c'est la meilleure pratique? Cela semblait juste parce que je retournais le type de données le plus générique tout en étant descriptif de ce qu'il fait, mais ce n'est peut-être pas correct de le faire.

92
KingNestor

Cela dépend vraiment de la raison pour laquelle vous utilisez cette interface spécifique.

Par exemple, IList<T> A plusieurs méthodes qui ne sont pas présentes dans IEnumerable<T>:

  • IndexOf(T item)
  • Insert(int index, T item)
  • RemoveAt(int index)

et propriétés:

  • T this[int index] { get; set; }

Si vous avez besoin de ces méthodes, retournez IList<T>.

De plus, si la méthode qui consomme votre résultat IEnumerable<T> Attend un IList<T>, Cela évitera au CLR de considérer toutes les conversions requises, optimisant ainsi le code compilé.

42
Jon Limjap

Les directives de conception du framework recommandent d'utiliser la classe Collection lorsque vous devez renvoyer une collection modifiable par l'appelant ou ReadOnlyCollection pour les collections en lecture seule.

La raison pour laquelle cela est préféré à un simple IList est que IList n'informe pas l'appelant s'il est en lecture seule ou non.

Si vous retournez un IEnumerable<T> au lieu de cela, certaines opérations peuvent être un peu plus compliquées à effectuer pour l'appelant. De plus, vous ne donnerez plus à l'appelant la flexibilité de modifier la collection, ce que vous voudrez peut-être ou non.

Gardez à l'esprit que LINQ contient quelques astuces dans sa manche et optimisera certains appels en fonction du type sur lequel ils sont effectués. Ainsi, par exemple, si vous effectuez un compte et que la collection sous-jacente est une liste, elle ne parcourra PAS tous les éléments.

Personnellement, pour un ORM, je resterais probablement avec Collection<T> comme valeur de retour.

63
Sam Saffron

En général, vous devez exiger le plus générique et renvoyer le plus spécifique possible. Donc, si vous avez une méthode qui prend un paramètre et que vous n'avez vraiment besoin que de ce qui est disponible dans IEnumerable, cela devrait être votre type de paramètre. Si votre méthode peut renvoyer un IList ou un IEnumerable, préférez renvoyer IList. Cela garantit qu'il est utilisable par le plus large éventail de consommateurs.

Soyez lâche dans ce dont vous avez besoin et explicite dans ce que vous fournissez.

20
Mel

Ça dépend...

Le retour du type le moins dérivé (IEnumerable) vous laissera le plus de latitude pour modifier l'implémentation sous-jacente sur la piste.

Le retour d'un type plus dérivé (IList) fournit aux utilisateurs de votre API plus d'opérations sur le résultat.

Je suggérerais toujours de renvoyer le type le moins dérivé qui a toutes les opérations dont vos utilisateurs auront besoin ... Donc, fondamentalement, vous devez d'abord déterminer quelles opérations sur le résultat ont du sens dans le contexte de l'API que vous définissez.

19
jerryjvl

Une chose à considérer est que si vous utilisez une instruction LINQ à exécution différée pour générer votre IEnumerable<T>, Appeler .ToList() avant de revenir de votre méthode signifie que vos éléments peuvent être itérés deux fois - une fois pour créer la liste, et une fois lorsque l'appelant parcourt, filtre ou transforme votre valeur de retour. Lorsque c'est possible, j'aime éviter de convertir les résultats de LINQ-to-Objects en une liste ou un dictionnaire concret jusqu'à ce que je le doive. Si mon appelant a besoin d'une liste, c'est un appel de méthode simple - je n'ai pas besoin de prendre cette décision pour eux, et cela rend mon code légèrement plus efficace dans les cas où l'appelant fait juste un foreach.

10
Joel Mueller

List<T> offre au code appelant de nombreuses autres fonctionnalités, telles que la modification de l'objet retourné et l'accès par index. La question se résume donc à: dans le cas d'utilisation spécifique de votre application, voulez-vous prendre en charge de telles utilisations (probablement en renvoyant une collection fraîchement construite!), Pour la commodité de l'appelant - ou voulez-vous la vitesse pour le cas simple lorsque toutes les l'appelant a besoin de parcourir la collection et vous pouvez retourner en toute sécurité une référence à une vraie collection sous-jacente sans craindre que cela ne la change par erreur, etc.?

Vous seul pouvez répondre à cette question, et uniquement en comprenant bien ce que vos appelants voudront faire avec la valeur de retour, et l'importance des performances ici (quelle est la taille des collections que vous copiez, quelle est la probabilité qu'il s'agisse d'un goulot d'étranglement, etc).

8
Alex Martelli

Je pense que vous pouvez utiliser l'un ou l'autre, mais chacun a une utilité. Fondamentalement, List est IEnumerable mais vous avez la fonctionnalité de comptage, ajouter un élément, supprimer un élément

IEnumerable n'est pas efficace pour compter les éléments

Si la collection est destinée à être en lecture seule, ou si la modification de la collection est contrôlée par Parent, renvoyer un IList juste pour Count n'est pas une bonne idée.

Dans Linq, il existe une méthode d'extension Count() sur IEnumerable<T> Qui, à l'intérieur du CLR, raccourcira vers .Count Si le type sous-jacent est de IList, donc les performances la différence est négligeable.

En général, je pense (opinion) qu'il est préférable de renvoyer IEnumerable si possible, si vous devez faire des ajouts, puis ajoutez ces méthodes à la classe parent, sinon le consommateur gère alors la collection dans Model, ce qui viole les principes, par exemple. manufacturer.Models.Add(model) viole la loi de déméter. Bien sûr, ce ne sont que des lignes directrices et non des règles dures et rapides, mais jusqu'à ce que vous ayez pleinement saisi l'applicabilité, il est préférable de suivre aveuglément que de ne pas suivre du tout.

public interface IManufacturer 
{
     IEnumerable<Model> Models {get;}
     void AddModel(Model model);
}

(Remarque: si vous utilisez nNHibernate, vous devrez peut-être mapper vers IList privé à l'aide de différents accesseurs.)

4
user53791

Ce n'est pas si simple lorsque vous parlez de valeurs de retour au lieu de paramètres d'entrée. Lorsqu'il s'agit d'un paramètre d'entrée, vous savez exactement ce que vous devez faire. Donc, si vous devez pouvoir parcourir la collection, vous prenez un IEnumberable alors que si vous devez ajouter ou supprimer, vous prenez un IList.

Dans le cas d'une valeur de retour, c'est plus difficile. Qu'attend votre interlocuteur? Si vous retournez un IEnumerable, il ne saura pas a priori qu'il peut en faire une IList. Mais, si vous retournez un IList, il saura qu'il peut le parcourir. Vous devez donc tenir compte de ce que votre correspondant va faire avec les données. La fonctionnalité dont votre interlocuteur a besoin/attend est ce qui devrait régir lors de la décision sur ce qu'il doit retourner.

1
JP Alioto

Je pense que vous pouvez utiliser l'un ou l'autre, mais chacun a une utilité. Fondamentalement, List est IEnumerable mais vous avez la fonctionnalité de comptage, Ajouter un élément, supprimer un élément

IEnumerable n'est pas efficace pour compter les éléments ou obtenir un élément spécifique dans la collection.

List est une collection qui est parfaitement adaptée à la recherche d'éléments spécifiques, faciles à ajouter ou à supprimer.

En général, j'essaie d'utiliser List dans la mesure du possible car cela me donne plus de flexibilité.

Utilisez List<FooBar> getRecentItems() plutôt que IList<FooBar> GetRecentItems()

0
Stuart

comme tous l'ont dit, cela dépend, si vous ne voulez pas ajouter/supprimer des fonctionnalités à la couche appelante, je voterai pour IEnumerable car il ne fournit que l'itération et les fonctionnalités de base que j'apprécie dans la conception. En retournant IList, mes votes sont toujours à nouveau, mais c'est surtout ce que vous aimez et ce qui ne l'est pas. en termes de performances, je pense qu'ils sont plus ou moins identiques.

0
Usman Masood

Si vous ne comptez pas dans votre code externe, il est toujours préférable de renvoyer IEnumerable, car plus tard, vous pouvez modifier votre implémentation (sans impact sur le code externe), par exemple, pour la logique yield iterator et conserver les ressources de mémoire ( très bonne fonction de langue d'ailleurs).

Cependant, si vous avez besoin du nombre d'éléments, n'oubliez pas qu'il existe une autre couche entre IEnumerable et IList - ICollection.

0
arbiter

Je suis peut-être un peu en retrait ici, vu que personne d'autre ne l'a suggéré jusqu'à présent, mais pourquoi ne pas renvoyer un (I)Collection<T>?

D'après ce dont je me souviens, Collection<T> était le type de retour préféré par rapport à List<T> car il résume l'implémentation. Ils implémentent tous IEnumerable, mais cela me semble un peu trop bas pour le travail.

0
Tiberiu Ana

Je pense que la règle générale est d'utiliser la classe plus spécifique pour revenir, pour éviter de faire un travail inutile et donner à votre appelant plus d'options.

Cela dit, je pense qu'il est plus important de considérer le code que vous écrivez devant vous que le code que le prochain gars écrira (dans des limites raisonnables.) C'est parce que vous pouvez faire des hypothèses sur le code qui existe déjà.

N'oubliez pas que le déplacement vers le haut d'une collection à partir de IEnumerable dans une interface fonctionnera, le déplacement vers le bas vers IEnumerable à partir d'une collection rompra le code existant.

Si toutes ces opinions semblent contradictoires, c'est parce que la décision est subjective.

0
Sprague