Ich habe mich gefragt, was genau die Methoden Where()
und ToList()
machen. Speziell habe ich mich gefragt, ob das Where()
ein neues Objekt im Speicher erstellen oder ein neues Objekt zurückgeben wird.
Ok, wenn ich mir den folgenden Code ansehe, sagen wir, dass ich eine Skelettprotokollklasse habe.
%Vor% Sagen Sie in meiner Geschäftslogik, dass ich nur Protokolle möchte, die von einem bestimmten Benutzer erstellt oder geändert wurden. Dies wird mit einer Methode erreicht: FilterLogsAccordingToUserId()
.
In dieser Situation ändert Where()
die IEnumerable<Log>
, indem alle Objekte entfernt werden, die nicht mit der Bedingung übereinstimmen, oder alle Objekte erfasst werden, dieses Objekt in eine Liste im Speicher umgewandelt wird und dieses neue Objekt zurückgegeben wird ?
Wenn es die zweite Möglichkeit ist, habe ich recht, wenn es um die Leistung geht, wenn eine ausreichend große Liste von Protokollen an die Funktion übergeben wird?
Nehmen wir die beiden Methoden getrennt.
Dieser gibt ein neues Objekt zurück, das bei der Aufzählung das ursprüngliche Sammlungsobjekt nach dem Prädikat filtert.
Es wird die ursprüngliche Sammlung in keiner Weise ändern, aber es wird mit ihr verknüpft .
Es ist auch eine aufgeschobene Ausführungsauflistung, was bedeutet, dass bis zur tatsächlichen Enumeration und bei jeder Aufzählung die ursprüngliche Sammlung verwendet und gefiltert wird.
Dies bedeutet, dass sich das gefilterte Ergebnis entsprechend ändert, wenn Sie die ursprüngliche Sammlung ändern.
Hier ist ein einfaches LINQPad Programm, das Folgendes demonstriert:
%Vor%Ausgabe:
Wie Sie sehen, fügt das Hinzufügen weiterer Elemente zur ursprünglichen Sammlung, die die Filterbedingungen der zweiten Sammlung erfüllt, diese Elemente auch in der gefilterten Sammlung erscheinen.
Dies erstellt ein neues Listenobjekt, füllt es mit der Sammlung und gibt diese Sammlung zurück.
Dies ist eine sofortige Methode, das heißt, sobald Sie diese Liste haben, ist sie jetzt eine vollständig separate Liste von der ursprünglichen Sammlung.
Beachten Sie, dass die Objekte in diese Liste möglicherweise noch mit der ursprünglichen Sammlung gemeinsam genutzt werden können. Die Methode ToList
erstellt keine neuen Kopien von allen, sondern die Sammlung ist ein neuer.
Hier ist ein einfaches LINQPad Programm, das Folgendes demonstriert:
%Vor%Ausgabe:
Hier sehen Sie, dass sich diese Liste nach der Erstellung nicht mehr ändert, wenn sich die ursprüngliche Sammlung ändert.
Sie können sich die Methode Where
als mit der ursprünglichen Sammlung verbunden vorstellen, während ToList
einfach eine neue Liste mit den Elementen zurückgibt und nicht mit der ursprünglichen Sammlung verknüpft ist.
Sehen wir uns nun Ihre letzte Frage an. Sollten Sie sich Sorgen um die Leistung machen? Nun, das ist ein ziemlich großes Thema, aber yes , Sie sollten sich Sorgen um die Leistung machen, aber nicht in einem solchen Ausmaß, dass Sie es die ganze Zeit tun.
Wenn Sie einem Where
-Aufruf eine große Sammlung geben, jede Zeit die Ergebnisse des Where
-Aufrufs aufzählen, werden Sie die ursprüngliche große Sammlung aufzählen und filtere es. Wenn der Filter nur einige dieser Elemente passieren lässt, zählt er bei jeder Enumeration immer noch über die ursprüngliche große Sammlung.
Auf der anderen Seite wird das Erstellen einer ToList
für etwas Großes auch eine große Liste erstellen.
Wird das ein Leistungsproblem sein?
Wer kann sagen, aber für alle Dinge Leistung, hier ist meine Nummer 1 Antwort:
Zu oft werden Sie feststellen, dass Programmierer sich über ein Stück Code ärgern, weil sie denken, dass es zu einem Leistungsproblem kommt, nur um vom langsamen Benutzer beim Betrachten des Bildschirms und der Downloadzeit der Daten in den Schatten gestellt zu werden oder nach der Zeit, die benötigt wird, um die Daten auf die Festplatte zu schreiben, oder was nicht.
Zuerst weißt du, dann reparierst du.
Where()
gibt ein neues IEnumerable
zurück. Es ist eine gefilterte Version (eine Projektion) der ursprünglichen Sequenz, und das Original bleibt unverändert. ToList()
gibt eine neue Liste mit der Projektion zurück.
Es ist auch wichtig zu beachten, dass der Aufruf von .Where()
die Projektion nicht auswertet, wenn der Enumerable aufgezählt wird. Zum Beispiel bei Verwendung in einer foreach
-Schleife oder in diesem Fall beim Aufruf von ToList()
.
Where
filtert ein IEnumerable<T>
, um nur die Elemente beizubehalten, die ein Prädikat erfüllen, und die Reihenfolge beizubehalten. Dies erzwingt nicht die Aufzählung der IEnumerable<T>
-Quelle, ist also deklarativer Natur.
ToList
konvertiert IEnumerable<T>
in ein List<T>
, um die Reihenfolge beizubehalten. Dies erzwingt die Aufzählung der gesamten Quelle IEnumerable<T>
.
In diesem Fall ändert .WHERE die IEnumerable-Protokolle, indem alle Objekte entfernt werden, die nicht mit der Bedingung übereinstimmen, oder ob alle Objekte aus den Protokollen imenumeriert werden, das Objekt in eine Liste im Speicher umgewandelt und dann zurückgegeben wird neues Objekt?
Ihre Abfrage der Form logs.Where(...).ToList()
streamt Ihre Log-Objekte durch den Where
-Teil und dann nur jene, die das Prädikat erfüllen, in das endgültige List<Log>
.
Where
erstellt einen Iterator, der über Ihre Sammlung aufzählt und nur die Elemente zurückgibt, die Ihrem Prädikat entsprechen. Das Wichtigste dabei ist, dass diese Iteration erst ausgeführt wird, wenn Sie tatsächlich versuchen, auf sie zuzugreifen (z. B. in einer foreach
-Schleife).
ToList
kopiert jedoch Verweise auf jedes Element in Ihrer aufzählbaren Auflistung in eine neue Liste (nicht Sie kopieren die Referenz nicht das Objekt selbst). Wenn Sie ein ToList
am Ende eines Where
anheften, wird% ce_de% durch die Sammlung iterieren müssen.
Kurz gesagt, wenn Sie Where
verwenden, erstellen Sie keine neuen Objekte (außer dem Iterator selbst) und Sie werden nichts in Ihrer ursprünglichen Sammlung ändern. Wenn Sie Where
verwenden, kopieren Sie die Referenzen auf die Objekte, die Ihrer ToList
-Klausel entsprechen, in eine neue Where
(die ursprüngliche Liste ist natürlich unverändert - es sei denn, Sie haben sie zugewiesen zurück zu derselben Variablen).
Wenn Sie also keine neue Liste erstellen müssen, verwenden Sie nicht List
. Wenn Sie nur Ihre Sammlung durchlaufen müssen, überspringen Sie einfach den ToList
-Teil. Es gibt jedoch einen subtilen Punkt, wenn Sie dies tun:
und ändern Sie dann Ihre Sammlung und dann tun Sie dies:
%Vor% Sie werden durch Ihre ursprüngliche Sammlung ( ToList
) iterieren, da es jetzt ist, nicht so wie es war, als Sie logs
deklariert haben.