IEnumerable comme type de retour
-
22-08-2019 - |
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.
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
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
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
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
Je reviens souvent IList
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.