Compte tenu de ce code:
IEnumerable<object> FilteredList()
{
foreach( object item in FullList )
{
if( IsItemInPartialList( item ) )
yield return item;
}
}
Pourquoi ne devrais-je pas simplement le coder de cette façon?:
IEnumerable<object> FilteredList()
{
var list = new List<object>();
foreach( object item in FullList )
{
if( IsItemInPartialList( item ) )
list.Add(item);
}
return list;
}
Je comprends en quelque sorte ce que fait le yield
mot-clé. Il dit au compilateur de construire un certain type de chose (un itérateur). Mais pourquoi l'utiliser? À part que ce soit un peu moins de code, qu'est-ce que ça fait pour moi?
FullList.Where(IsItemInPartialList)
:)Réponses:
Utiliser
yield
rend la collection paresseuse.Disons que vous n'avez besoin que des cinq premiers éléments. À votre façon, je dois parcourir toute la liste pour obtenir les cinq premiers éléments. Avec
yield
, je ne boucle que les cinq premiers éléments.la source
FullList.Where(IsItemInPartialList)
sera tout aussi paresseuse. Seulement, il nécessite beaucoup moins de code personnalisé --- gunk --- généré par le compilateur. Et moins de temps pour les développeurs pour l'écriture et la maintenance. (Bien sûr, c'était juste cet exemple)yield return
) dans la mesure du possible.L'avantage des blocs d'itérateur est qu'ils fonctionnent paresseusement. Vous pouvez donc écrire une méthode de filtrage comme celle-ci:
Cela vous permettra de filtrer un flux aussi longtemps que vous le souhaitez, sans jamais mettre en mémoire tampon plus d'un élément à la fois. Si vous n'avez besoin que de la première valeur de la séquence renvoyée, par exemple, pourquoi voudriez-vous tout copier dans une nouvelle liste?
Comme autre exemple, vous pouvez facilement créer un flux infini à l' aide de blocs d'itérateur. Par exemple, voici une séquence de nombres aléatoires:
Comment stockeriez-vous une séquence infinie dans une liste?
Ma série de blogs Edulinq donne un exemple d'implémentation de LINQ to Objects qui fait un usage intensif des blocs d'itérateur. LINQ est fondamentalement paresseux là où il peut être - et mettre des choses dans une liste ne fonctionne tout simplement pas de cette façon.
la source
RandomSequence
ou non. Pour moi, IEnumerable signifie - d'abord et avant tout - que je peux itérer avec foreach, mais cela conduirait évidemment à une boucle infinie ici. Je considérerais cela comme une mauvaise utilisation assez dangereuse du concept IEnumerable, mais YMMV.IEnumerable<BigInteger>
représentation de la séquence de Fibonacci, par exemple. Vous pouvez l'utiliserforeach
avec, mais rien neIEnumerable<T>
garantit qu'il sera fini.Avec le code «liste», vous devez traiter la liste complète avant de pouvoir la passer à l'étape suivante. La version "yield" passe immédiatement l'article traité à l'étape suivante. Si cette "prochaine étape" contient un ".Take (10)" alors la version "yield" ne traitera que les 10 premiers éléments et oubliera le reste. Le code "liste" aurait tout traité.
Cela signifie que vous voyez la plus grande différence lorsque vous devez effectuer beaucoup de traitement et / ou avoir de longues listes d'éléments à traiter.
la source
Vous pouvez utiliser
yield
pour renvoyer des éléments qui ne figurent pas dans une liste. Voici un petit exemple qui pourrait parcourir une liste à l'infini jusqu'à son annulation.Cela écrit
...etc. à la console jusqu'à son annulation.
la source
Lorsque le code ci-dessus est utilisé pour parcourir FilteredList () et en supposant que item.Name == "James" sera satisfait sur le 2ème élément de la liste, la méthode utilisant
yield
donnera deux fois. C'est un comportement paresseux.Où, la méthode utilisant list ajoutera tous les n objets à la liste et passera la liste complète à la méthode appelante.
C'est exactement un cas d'utilisation où la différence entre IEnumerable et IList peut être mise en évidence.
la source
Le meilleur exemple du monde réel que j'ai vu pour l'utilisation de
yield
serait de calculer une séquence de Fibonacci.Considérez le code suivant:
Cela retournera:
C'est bien car cela vous permet de calculer rapidement et facilement une série infinie, vous donnant la possibilité d'utiliser les extensions Linq et d'interroger uniquement ce dont vous avez besoin.
la source
Parfois, c'est utile, parfois non. Si l'ensemble complet de données doit être examiné et renvoyé, il n'y aura aucun avantage à utiliser le rendement, car il n'a fait qu'introduire des frais généraux.
Lorsque le rendement brille vraiment, c'est quand seul un ensemble partiel est renvoyé. Je pense que le meilleur exemple est le tri. Supposons que vous ayez une liste d'objets contenant une date et un montant en dollars de cette année et que vous aimeriez voir les premiers (5) enregistrements de l'année.
Pour ce faire, la liste doit être triée par ordre croissant de date, puis avoir les 5 premières prises. Si cela était fait sans rendement, la liste entière devrait être triée, jusqu'à s'assurer que les deux dernières dates étaient dans l'ordre.
Cependant, avec le rendement, une fois les 5 premiers articles établis, le tri s'arrête et les résultats sont disponibles. Cela peut gagner beaucoup de temps.
la source
L'instruction yield return vous permet de ne renvoyer qu'un seul article à la fois. Vous collectez tous les éléments d'une liste et renvoyez à nouveau cette liste, ce qui représente une surcharge de mémoire.
la source