Le mot-clé yield est l'un de ces mots-clés en C # qui continue de me mystifier, et je n'ai jamais été sûr de l'utiliser correctement.
Parmi les deux morceaux de code suivants, lequel est préféré et pourquoi?
Version 1: utilisation du rendement
public static IEnumerable<Product> GetAllProducts()
{
using (AdventureWorksEntities db = new AdventureWorksEntities())
{
var products = from product in db.Product
select product;
foreach (Product product in products)
{
yield return product;
}
}
}
Version 2: renvoyer la liste
public static IEnumerable<Product> GetAllProducts()
{
using (AdventureWorksEntities db = new AdventureWorksEntities())
{
var products = from product in db.Product
select product;
return products.ToList<Product>();
}
}
c#
yield-return
senfo
la source
la source
yield
est lié àIEnumerable<T>
et son genre. C'est en quelque sorte une évaluation paresseuseyield return
si le code qui réitère les résultatsGetAllProducts()
permet à l'utilisateur d'annuler prématurément le traitement.Réponses:
J'ai tendance à utiliser le rendement-retour lorsque je calcule l'élément suivant dans la liste (ou même le groupe suivant d'éléments).
En utilisant votre version 2, vous devez avoir la liste complète avant de revenir. En utilisant yield-return, vous n'avez vraiment besoin que de l'article suivant avant de retourner.
Entre autres choses, cela permet de répartir le coût de calcul des calculs complexes sur une période plus longue. Par exemple, si la liste est connectée à une interface graphique et que l'utilisateur ne passe jamais à la dernière page, vous ne calculez jamais les éléments finaux de la liste.
Un autre cas où le rendement-retour est préférable est si le IEnumerable représente un ensemble infini. Considérez la liste des nombres premiers ou une liste infinie de nombres aléatoires. Vous ne pouvez jamais renvoyer l'IEnumerable complet à la fois, vous utilisez donc yield-return pour renvoyer la liste de manière incrémentielle.
Dans votre exemple particulier, vous avez la liste complète des produits, donc j'utiliserais la version 2.
la source
Yield return
semble être un raccourci pour écrire votre propre classe d'itérateur personnalisée (implémenter IEnumerator). Par conséquent, les avantages mentionnés s'appliquent également aux classes d'itérateurs personnalisés. Quoi qu'il en soit, les deux constructions conservent un état intermédiaire. Dans sa forme la plus simple, il s'agit de conserver une référence à l'objet actuel.Remplir une liste temporaire, c'est comme télécharger la vidéo entière, alors que l'utiliser,
yield
c'est comme diffuser cette vidéo.la source
Comme exemple conceptuel pour comprendre quand vous devez utiliser
yield
, disons que la méthodeConsumeLoop()
traite les éléments retournés / produits parProduceList()
:Sans
yield
, l'appel àProduceList()
pourrait prendre beaucoup de temps car vous devez compléter la liste avant de revenir:En utilisant
yield
, il se réorganise, sorte de travailler "en parallèle":Et enfin, comme beaucoup l'ont déjà suggéré, vous devriez utiliser la version 2 car vous avez de toute façon déjà la liste complète.
la source
Je sais que c'est une vieille question, mais j'aimerais donner un exemple de la façon dont le mot clé yield peut être utilisé de manière créative. J'ai vraiment profité de cette technique. J'espère que cela sera utile à quiconque trébuchera sur cette question.
Remarque: Ne pensez pas que le mot clé yield est simplement une autre façon de créer une collection. Une grande partie de la puissance du rendement vient du fait que l'exécution est suspendue dans votre méthode ou votre propriété jusqu'à ce que le code appelant répète la valeur suivante. Voici mon exemple:
L'utilisation du mot clé yield (à côté de l'implémentation Caliburn.Micro coroutines de Rob Eisenburg ) me permet d'exprimer un appel asynchrone à un service Web comme celui-ci:
Ce que cela va faire est d'allumer mon BusyIndicator, d'appeler la méthode Login sur mon service Web, de définir mon indicateur IsLoggedIn sur la valeur de retour, puis de désactiver le BusyIndicator.
Voici comment cela fonctionne: IResult a une méthode Execute et un événement Completed. Caliburn.Micro récupère l'IEnumerator de l'appel à HandleButtonClick () et le transmet à une méthode Coroutine.BeginExecute. La méthode BeginExecute commence l'itération à travers les IResults. Lorsque le premier IResult est renvoyé, l'exécution est suspendue dans HandleButtonClick () et BeginExecute () attache un gestionnaire d'événements à l'événement Completed et appelle Execute (). IResult.Execute () peut effectuer une tâche synchrone ou asynchrone et déclenche l'événement Completed lorsqu'il est terminé.
LoginResult ressemble à ceci:
Il peut être utile de configurer quelque chose comme ça et de parcourir l'exécution pour voir ce qui se passe.
J'espère que cela aide quelqu'un! J'ai vraiment aimé explorer les différentes façons dont le rendement peut être utilisé.
la source
yield
de cette façon. Cela semble être une manière élégante d'émuler le modèle asynchrone / attente (qui, je suppose, serait utilisé à la place deyield
si cela était réécrit aujourd'hui). Avez-vous constaté que ces utilisations créativesyield
ont produit (sans jeu de mots) des rendements décroissants au fil des ans, car C # a évolué depuis que vous avez répondu à cette question? Ou proposez-vous toujours des cas d'utilisation intelligents modernisés comme celui-ci? Et si oui, cela vous dérangerait-il de partager un autre scénario intéressant pour nous?Cela va sembler être une suggestion bizarre, mais j'ai appris à utiliser le
yield
mot clé en C # en lisant une présentation sur les générateurs en Python: http://www.dabeaz.com/generators/Generators.pdf de David M. Beazley . Vous n'avez pas besoin de connaître beaucoup Python pour comprendre la présentation - je ne l'ai pas fait. Je l'ai trouvé très utile pour expliquer non seulement comment fonctionnent les générateurs, mais pourquoi vous devriez vous en soucier.la source
Le rendement peut être très puissant pour les algorithmes où vous devez parcourir plusieurs millions d'objets. Prenons l'exemple suivant où vous devez calculer les trajets possibles pour le covoiturage. D'abord, nous générons des voyages possibles:
Ensuite, parcourez chaque voyage:
Si vous utilisez List au lieu de yield, vous devrez allouer 1 million d'objets à la mémoire (~ 190 Mo) et cet exemple simple prendra ~ 1400 ms pour s'exécuter. Cependant, si vous utilisez yield, vous n'avez pas besoin de mettre tous ces objets temporaires en mémoire et vous obtiendrez une vitesse d'algorithme considérablement plus rapide: cet exemple ne prendra que ~ 400 ms pour fonctionner sans aucune consommation de mémoire.
la source
yield
fonctionne sous les couvertures en implémentant une machine d'état en interne. Voici une réponse SO avec 3 articles de blog MSDN détaillés qui expliquent l'implémentation en détail. Écrit par Raymond Chen @ MSFTLes deux morceaux de code font vraiment deux choses différentes. La première version attirera les membres selon vos besoins. La deuxième version chargera tous les résultats en mémoire avant de commencer à en faire quoi que ce soit.
Il n'y a pas de bonne ou de mauvaise réponse à celle-ci. Laquelle est préférable dépend simplement de la situation. Par exemple, s'il y a une limite de temps pour terminer votre requête et que vous devez faire quelque chose de semi-compliqué avec les résultats, la deuxième version pourrait être préférable. Mais méfiez-vous des grands ensembles de résultats, surtout si vous exécutez ce code en mode 32 bits. J'ai été mordu par les exceptions OutOfMemory à plusieurs reprises lors de l'exécution de cette méthode.
La chose clé à garder à l'esprit est la suivante: les différences résident dans l'efficacité. Ainsi, vous devriez probablement choisir celui qui simplifie votre code et le modifier uniquement après le profilage.
la source
Le rendement a deux grandes utilisations
Il permet de fournir une itération personnalisée sans créer de collections temporaires. (chargement de toutes les données et mise en boucle)
Cela aide à effectuer une itération avec état. ( diffusion)
Ci-dessous est une vidéo simple que j'ai créée avec une démonstration complète afin de soutenir les deux points ci-dessus
http://www.youtube.com/watch?v=4fju3xcm21M
la source
C'est ce que Chris Sells raconte à propos de ces instructions dans le langage de programmation C # ;
la source
F().Any()
- cela reviendra après avoir essayé d'énumérer le premier résultat uniquement. En général, vous ne devez pas vous fier à unIEnumerable yield
pour changer l'état du programme, car il peut ne pas être déclenchéEn supposant que la classe LINQ de vos produits utilise un rendement similaire pour l'énumération / itération, la première version est plus efficace car elle ne produit qu'une seule valeur à chaque itération.
Le deuxième exemple convertit l'énumérateur / itérateur en une liste avec la méthode ToList (). Cela signifie qu'il itère manuellement sur tous les éléments de l'énumérateur, puis renvoie une liste plate.
la source
C'est un peu plus que le point, mais comme la question porte sur les meilleures pratiques, je vais continuer et mettre mes deux cents. Pour ce type de chose, je préfère grandement en faire une propriété:
Bien sûr, c'est un peu plus de plaque chauffante, mais le code qui utilise cela aura l'air beaucoup plus propre:
contre
Remarque: je ne ferais pas cela pour les méthodes qui peuvent prendre un certain temps pour faire leur travail.
la source
Et qu'en est-il?
Je suppose que c'est beaucoup plus propre. Cependant, je n'ai pas VS2008 à portée de main pour vérifier. Dans tous les cas, si Products implémente IEnumerable (comme il semble - il est utilisé dans une instruction foreach), je le retournerais directement.
la source
J'aurais utilisé la version 2 du code dans ce cas. Étant donné que vous disposez de la liste complète des produits disponibles et que c'est ce que le «consommateur» attend de cet appel de méthode, il serait nécessaire de renvoyer les informations complètes à l'appelant.
Si l'appelant de cette méthode requiert "une" information à la fois et que la consommation des informations suivantes est à la demande, il serait alors utile d'utiliser return return qui s'assurera que la commande d'exécution sera retournée à l'appelant lorsque une unité d'information est disponible.
Voici quelques exemples où l'on pourrait utiliser le rendement du rendement:
Pour répondre à vos questions, j'aurais utilisé la version 2.
la source
Renvoyez la liste directement. Avantages:
La liste est réutilisable. (l'itérateur n'est pas)pas vraiment vrai, merci JonVous devez utiliser l'itérateur (yield) à partir du moment où vous pensez que vous n'aurez probablement pas à répéter jusqu'à la fin de la liste, ou lorsqu'il n'aura pas de fin. Par exemple, le client appelant va rechercher le premier produit qui satisfait un prédicat, vous pouvez envisager d'utiliser l'itérateur, bien que ce soit un exemple artificiel, et il existe probablement de meilleures façons de le faire. Fondamentalement, si vous savez à l'avance que toute la liste devra être calculée, faites-le tout de suite. Si vous pensez que ce ne sera pas le cas, envisagez d'utiliser la version itérateur.
la source
La phrase clé return return est utilisée pour maintenir la machine d'état pour une collection particulière. Partout où le CLR voit la phrase clé return return utilisée, CLR implémente un modèle Enumerator sur ce morceau de code. Ce type d'implémentation aide le développeur de tout type de plomberie que nous aurions autrement dû faire en l'absence du mot-clé.
Supposons que le développeur filtre une collection, effectue une itération dans la collection, puis extrait ces objets dans une nouvelle collection. Ce type de plomberie est assez monotone.
En savoir plus sur le mot clé ici dans cet article .
la source
L'utilisation de yield est similaire au mot-clé return , sauf qu'il renvoie un générateur . Et l' objet générateur ne traversera qu'une seule fois .
le rendement a deux avantages:
Il y a une autre explication claire qui peut vous aider.
la source