Angesichts dieses Codes:
IEnumerable<object> FilteredList()
{
foreach( object item in FullList )
{
if( IsItemInPartialList( item ) )
yield return item;
}
}
Warum sollte ich es nicht einfach so codieren?:
IEnumerable<object> FilteredList()
{
var list = new List<object>();
foreach( object item in FullList )
{
if( IsItemInPartialList( item ) )
list.Add(item);
}
return list;
}
Ich verstehe irgendwie, was das yield
Schlüsselwort bewirkt. Es weist den Compiler an, eine bestimmte Art von Dingen (einen Iterator) zu erstellen. Aber warum es benutzen? Was macht es für mich, abgesehen davon, dass es etwas weniger Code ist?
FullList.Where(IsItemInPartialList)
:)Antworten:
Die Verwendung
yield
macht die Sammlung faul.Angenommen, Sie benötigen nur die ersten fünf Elemente. Auf Ihre Weise muss ich die gesamte Liste durchlaufen , um die ersten fünf Elemente zu erhalten. Mit
yield
schleife ich nur die ersten fünf Elemente durch.quelle
FullList.Where(IsItemInPartialList)
genauso faul ist. Nur erfordert es weit weniger vom Compiler generierten benutzerdefinierten --- Gunk --- Code. Und weniger Entwicklerzeit beim Schreiben und Verwalten. (Natürlich war das nur dieses Beispiel)yield return
wo immer möglich , die verzögerte Ausführung ( ) verwendet.Der Vorteil von Iteratorblöcken besteht darin, dass sie träge arbeiten. Sie können also eine Filtermethode wie folgt schreiben:
Auf diese Weise können Sie einen Stream so lange filtern, wie Sie möchten, und niemals mehr als ein einzelnes Element gleichzeitig puffern. Wenn Sie beispielsweise nur den ersten Wert aus der zurückgegebenen Sequenz benötigen, warum sollten Sie dann alles in eine neue Liste kopieren ?
Als weiteres Beispiel können Sie mit Iteratorblöcken einfach einen unendlichen Stream erstellen . Hier ist zum Beispiel eine Folge von Zufallszahlen:
Wie würden Sie eine unendliche Sequenz in einer Liste speichern?
Meine Edulinq Blog - Serie gibt eine Beispielimplementierung von LINQ to Objects , die macht schwere Verwendung von Iterator Blöcke. LINQ ist grundsätzlich faul, wo es sein kann - und Dinge in eine Liste aufzunehmen, funktioniert einfach nicht so.
quelle
RandomSequence
oder nicht. IEnumerable bedeutet für mich in erster Linie, dass ich mit foreach iterieren kann, aber dies würde hier offensichtlich zu einer unendlichen Schleife führen. Ich würde dies als einen ziemlich gefährlichen Missbrauch des IEnumerable-Konzepts betrachten, aber YMMV.IEnumerable<BigInteger>
Darstellung der Fibonacci-Sequenz erstellen . Sie könnenforeach
es verwenden, aber nichts überIEnumerable<T>
garantiert, dass es endlich sein wird.Mit dem "Listen" -Code müssen Sie die vollständige Liste verarbeiten, bevor Sie sie an den nächsten Schritt weitergeben können. Die "Yield" -Version übergibt den verarbeiteten Artikel sofort an den nächsten Schritt. Wenn dieser "nächste Schritt" ein ".Take (10)" enthält, verarbeitet die "Yield" -Version nur die ersten 10 Elemente und vergisst den Rest. Der "Listen" -Code hätte alles verarbeitet.
Dies bedeutet, dass Sie den größten Unterschied sehen, wenn Sie viel verarbeiten müssen und / oder lange Listen mit zu verarbeitenden Elementen haben.
quelle
Sie können
yield
Elemente zurückgeben, die nicht in einer Liste enthalten sind. Hier ist ein kleines Beispiel, das eine Liste unendlich durchlaufen kann, bis es abgebrochen wird.Das schreibt
...etc. an die Konsole bis zum Abbruch.
quelle
Wenn der obige Code zum Durchlaufen von FilteredList () verwendet wird und davon ausgegangen wird, dass item.Name == "James" für das zweite Element in der Liste erfüllt ist, ergibt die verwendete Methode
yield
zweimal. Dies ist ein faules Verhalten.Wobei als Methode mit Liste alle n Objekte zur Liste hinzugefügt und die vollständige Liste an die aufrufende Methode übergeben werden.
Dies ist genau ein Anwendungsfall, bei dem der Unterschied zwischen IEnumerable und IList hervorgehoben werden kann.
quelle
Das beste Beispiel aus der realen Welt, das ich für die Verwendung gesehen habe,
yield
wäre die Berechnung einer Fibonacci-Sequenz.Betrachten Sie den folgenden Code:
Dies wird zurückkehren:
Dies ist hilfreich, da Sie so schnell und einfach eine unendliche Reihe berechnen können, sodass Sie die Linq-Erweiterungen verwenden und nur das abfragen können, was Sie benötigen.
quelle
Manchmal ist es nützlich, manchmal nicht. Wenn der gesamte Datensatz geprüft und zurückgegeben werden muss, hat die Verwendung von Yield keinen Vorteil, da lediglich der Overhead eingeführt wurde.
Wenn der Ertrag wirklich glänzt, wird nur ein Teilsatz zurückgegeben. Ich denke, das beste Beispiel ist das Sortieren. Angenommen, Sie haben eine Liste von Objekten, die ein Datum und einen Dollarbetrag aus diesem Jahr enthalten, und Sie möchten die ersten wenigen (5) Datensätze des Jahres sehen.
Um dies zu erreichen, muss die Liste nach Datum aufsteigend sortiert werden und dann müssen die ersten 5 genommen werden. Wenn dies ohne Ertrag erfolgen würde, müsste die gesamte Liste sortiert werden, um sicherzustellen, dass die letzten beiden Daten in Ordnung sind.
Mit der Ausbeute stoppt die Sortierung jedoch, sobald die ersten 5 Elemente festgelegt wurden, und die Ergebnisse sind verfügbar. Dies kann viel Zeit sparen.
quelle
Mit der Yield Return-Anweisung können Sie jeweils nur einen Artikel zurückgeben. Sie sammeln alle Elemente in einer Liste und geben diese Liste erneut zurück. Dies ist ein Speicheraufwand.
quelle