Question

Y at-il un problème avec l'utilisation IEnumerable<T> comme un type de retour? FxCop se plaint de retour List<T> (il conseille de revenir à la place Collection<T>).

Eh bien, je l'ai toujours été guidé par une règle « accepter le moins possible, mais revenir au maximum. »

De ce point de vue, le retour IEnumerable<T> est une mauvaise chose, mais que dois-je faire quand je veux utiliser « récupération paresseux »? De plus, le mot-clé yield est un goodie.

Était-ce utile?

La solution

Ceci est vraiment une question en deux parties.

1) Est-il en soi quelque chose de mal avec le retour d'un IEnumerable

Non, rien du tout. En fait, si vous utilisez C # itérateurs c'est le comportement attendu. La conversion à une liste ou d'une autre classe de collection est préemptive pas une bonne idée. Cela fait une hypothèse sur le mode d'utilisation par votre correspondant. Je trouve que ce n'est pas une bonne idée de prendre quoi que ce soit au sujet de l'appelant. Ils peuvent avoir de bonnes raisons pour lesquelles ils veulent un IEnumerable . Peut-être qu'ils veulent le convertir en une hiérarchie de collection complètement différente (dans ce cas, une conversion à la liste est gaspillée).

2) Y a-t-il des circonstances où il peut être préférable de retourner autre chose que IEnumerable ?

Oui. Bien que ce n'est pas une bonne idée de prendre beaucoup sur vos interlocuteurs, il est parfaitement normal de prendre des décisions en fonction de votre propre comportement. Imaginez un scénario dans lequel vous aviez un objet multi-thread qui a été faire la queue demande dans un objet qui a été constamment mis à jour. Dans ce cas, le retour IEnumerable brut est irresponsable. Dès que la collection est modifié le dénombrable est invalidée et provoquera une Execption de se produire. Au lieu de cela, vous pouvez prendre un instantané de la structure et retourner cette valeur. Dites sous une forme Liste . Dans ce cas, je voudrais juste revenir l'objet que la structure directe (ou interface).

Ceci est certainement le cas plus rare cependant.

Autres conseils

Non, IEnumerable<T> est bon chose à revenir ici, car tout ce que vous promettez est « une séquence de valeurs (dactylographiées) ». Idéal pour LINQ etc, et parfaitement utilisable.

L'appelant peut facilement mettre ces données dans une liste (ou autre) -. En particulier avec LINQ (de ToList, ToArray, etc)

Cette approche vous permet de Spool paresseusement des valeurs, plutôt que d'avoir à mémoire tampon toutes les données. Certainement un goodie. J'ai écrit-up une autre astuce utile IEnumerable<T> l'autre jour , aussi.

A propos de votre principe. « Accepter le moins possible, mais revenir au maximum »

La clé de la gestion de la complexité d'un vaste programme est une technique appelée cacher l'information . Si votre méthode fonctionne en construisant un List<T>, il est souvent nécessaire de ne pas révéler ce fait en retournant ce type. Si vous le faites, vos correspondants peuvent modifier la liste leur retour. Cela supprime votre capacité à faire la mise en cache ou itération paresseux avec yield return.

Alors un meilleur principe est une fonction à suivre est:. « Révéler aussi peu que possible sur la façon dont vous travaillez »

IEnumerable est bien pour moi, mais il a quelques inconvénients. Le client doit énumérer pour obtenir les résultats. Il n'a aucun moyen de vérifier le comte etc. Liste est mauvais parce que vous exposez trop de contrôle; le client peut ajouter / retirer etc. de celui-ci et qui peut être une mauvaise chose. Collection semble le meilleur compromomise, au moins dans la vue de FxCop. J'utilise AllWays ce qui semble dans mon contexte correspond le (par exemple. Si je veux revenir en lecture seule collection i collection expose le type de retour et retour List.AsReadOnly () ou IEnumerable pour l'évaluation paresseuse au moyen du rendement, etc.). Prenez-le sur un cas par cas

« accepter le moins possible, mais revenir au maximum » est ce que je préconise. Lorsqu'une méthode retourne un objet, que les justifications que nous avons de ne pas renvoyer le type réel et de limiter les capacités de l'objet en retournant un type de base. Cela soulève cependant une question de savoir comment pouvons-nous savoir ce que le (type réel) « maximum » sera quand nous concevons une interface. La réponse est très simple. Seulement dans des cas extrêmes où le concepteur d'interface est la conception d'une interface ouverte, qui sera mis en œuvre en dehors de l'application / composant, ils ne savent pas ce que le type de rendement réel peut être. Un concepteur intelligent devrait toujours prendre en considération ce que la méthode devrait faire et quel type de rendement optimal / générique devrait être.

par exemple. Si je suis la conception d'une interface pour récupérer un vecteur d'objets, et je sais que le nombre d'objets retournés vont être variable, je vais toujours supposer un développeur intelligent utilisera toujours une liste. Si quelqu'un envisage de retourner un tableau, je remets en question ses capacités, à moins qu'il / elle vient de renvoyer les données d'une autre couche qu'il / elle ne possède pas. Et ce qui est probablement la raison pour laquelle FxCop plaide pour ICollection (base commune pour List et Array).

Le ci-dessus étant dit, il y a deux autres choses à considérer

  • si les données renvoyées doivent être mutable ou immuable

  • si les données renvoyées sont partagées entre plusieurs appelants

En ce qui concerne les évaluations paresseux LINQ je suis sûr que 95% + C # utilisateurs ne comprennent pas les successions ab intestat. Il est donc non-oo-ish. OO favorise les changements d'état de béton sur les invocations de méthodes. évaluation LINQ paresseux favorise les changements d'état d'exécution sur le modèle d'évaluation d'expression (pas quelque chose que les utilisateurs non avancés suivent toujours).

De retour IEnumerable est OK si vous êtes vraiment seulement retourner une énumération, et il sera consommé par votre interlocuteur en tant que tel.

Mais comme d'autres le font remarquer, il présente l'inconvénient que l'appelant peut avoir besoin d'énumérer s'il a besoin d'autres informations (par exemple le comte). La méthode d'extension .NET 3.5 IEnumerable .Count énumérera dans les coulisses si la valeur de retour ne met pas en œuvre ICollection , qui peut être indésirable.

Je reviens souvent IList ou ICollection lorsque le résultat est une collection - interne méthode peut utiliser une liste et soit le renvoyer en l'état, ou le retour Liste si vous .AsReadOnly vouloir protéger contre toute modification (par exemple si vous cache la liste interne). FxCop est tout à fait autant que je sache heureux avec une ou l'autre de ces derniers.

Un aspect important est que lorsque vous revenez un List<T> vous êtes réel retour d'une référence . Cela permet à un appelant de manipuler votre liste. Ce problème est-par exemple commune, une couche d'affaires qui retourne une List<T> à une couche graphique.

Juste parce que vous dites que vous êtes de retour IEnumerable ne signifie pas que vous ne pouvez pas retourner une liste. L'idée est de réduire le couplage non nécessaire. Tout ce que l'appelant devrait se soucier est d'obtenir une liste des choses, plutôt que le type exact de collection utilisée pour contenir cette liste. Si vous avez quelque chose qui est soutenu par un tableau, puis obtenir quelque chose comme le comte va être rapide de toute façon.

Je pense que votre orientation est grand - si vous êtes en mesure d'être plus précis sur ce que vous êtes de retour sans perte de performance (vous ne devez pas construire par exemple une liste de votre résultat), faites-le. Mais si votre fonction ne sait pas légitimement quel type il va trouver, comme si, dans certaines situations, vous allez travailler avec une liste et dans certains avec un tableau, etc., puis retour IEnumerable est le « meilleur » que vous pouvez faire . Pensez-y comme le « plus grand commun multiple » de tout ce que vous voudrez peut-être revenir.

Je ne peux pas accepter la réponse choisie. Il y a plusieurs façons de traiter le scénario décrit, mais en utilisant une liste ou tout ce que votre utilisation de n'est pas un d'entre eux. Au moment où le IEnumerable est retourné, vous devez supposer que l'appelant pourrait faire un foreach. Dans ce cas, il n'a pas d'importance si le type de béton est Liste ou des spaghettis. En fait, juste indexation est un problème surtout si des éléments sont supprimés.

Toute valeur retournée est un instantané. Il peut être le contenu actuel du IEnumerable auquel cas si elle est mise en mémoire cache, il devrait être un clone de la copie en cache; si elle est censée être plus dynamique (comme les resuts d'une requête SQL) puis utilisez le retour de rendement; mais permettant le récipient de muter à volonté et fournir des méthodes telles que le comte et indexeur est une recette pour un désastre dans un monde multithread. Je ne l'ai pas encore entré dans la capacité de l'appelant d'appeler Ajouter ou Supprimer sur un conteneur de votre code est censé être en contrôle de.

retour également un verrouillage de type béton vous dans une mise en œuvre. Aujourd'hui, vous pouvez être en interne en utilisant une liste. Demain peut-être vous ne devenez multithread et que vous souhaitez utiliser un fil contenant sécuritaire ou un tableau ou une file d'attente ou les valeurs collection d'un dictionnaire ou la sortie d'une requête Linq. Si vous vous enfermez dans un type de retour concret alors vous devez soit changer un tas de code ou de faire une conversion avant de revenir.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top