Ich habe einige Zweifel an der Funktionsweise von Enumeratoren und LINQ. Betrachten Sie diese beiden einfachen Auswahlmöglichkeiten:
List<Animal> sel = (from animal in Animals
join race in Species
on animal.SpeciesKey equals race.SpeciesKey
select animal).Distinct().ToList();
oder
IEnumerable<Animal> sel = (from animal in Animals
join race in Species
on animal.SpeciesKey equals race.SpeciesKey
select animal).Distinct();
Ich habe die Namen meiner ursprünglichen Objekte geändert, damit dies wie ein allgemeineres Beispiel aussieht. Die Abfrage selbst ist nicht so wichtig. Was ich fragen möchte, ist Folgendes:
foreach (Animal animal in sel) { /*do stuff*/ }
Mir ist aufgefallen, dass
IEnumerable
beim Debuggen und Überprüfen von "sel", in diesem Fall IEnumerable, einige interessante Elemente angezeigt werden: "inner", "Outer", "innerKeySelector" und "OuterKeySelector". Diese beiden letzten Elemente werden angezeigt Delegierte sein. Das "innere" Mitglied enthält keine "Tier" -Instanzen, sondern "Spezies" -Instanzen, was für mich sehr seltsam war. Das "äußere" Mitglied enthält "Tier" -Instanzen. Ich gehe davon aus, dass die beiden Delegierten bestimmen, was rein und was raus geht.Ich habe festgestellt, dass bei Verwendung von "Distinct" das "Inner" 6 Elemente enthält (dies ist falsch, da nur 2 Distinct sind), das "Outer" jedoch die richtigen Werte enthält. Auch hier bestimmen wahrscheinlich die delegierten Methoden dies, aber dies ist etwas mehr als ich über IEnumerable weiß.
Welche der beiden Optionen ist vor allem in Bezug auf die Leistung die beste?
Die böse Listenkonvertierung über .ToList()
?
Oder vielleicht den Enumerator direkt benutzen?
Wenn Sie können, erklären Sie bitte auch ein wenig oder werfen Sie einige Links, die diese Verwendung von IEnumerable erklären.
quelle
IEnumerable<T>
wird nach dem ersten Teil LINQ-To-Objects sein, was bedeutet, dass alle entdeckten Objekte zurückgegeben werden müssten, um Feline auszuführen. Auf der anderen SeiteIQuertable<T>
kann die Abfrage verfeinert werden, wobei nur Spotted Felines heruntergezogen werden.Es gibt einen sehr guten Artikel von: Claudio Bernasconis TechBlog hier: Wann man IEnumerable, ICollection, IList und List verwendet
Hier einige grundlegende Punkte zu Szenarien und Funktionen:
quelle
List
ist eine Implementierung vonIList
und hat als solche eine zusätzliche Funktionalität auf der inIList
(zSort
,Find
,InsertRange
). Wenn Sie sich selbst Gewalt anzuwenden , umIList
überList
, lösen Sie diese Methoden , die Sie benötigenIReadOnlyCollection<T>
[]
einzufügen.Mit einer implementierten Klasse
IEnumerable
können Sie dieforeach
Syntax verwenden.Grundsätzlich gibt es eine Methode, um das nächste Element in der Sammlung zu erhalten. Es muss nicht die gesamte Sammlung gespeichert werden und es ist nicht bekannt, wie viele Elemente sich darin befinden. Es wird
foreach
nur das nächste Element abgerufen, bis es aufgebraucht ist.Dies kann unter bestimmten Umständen sehr nützlich sein, z. B. in einer massiven Datenbanktabelle, in der Sie nicht das gesamte Objekt in den Speicher kopieren möchten, bevor Sie mit der Verarbeitung der Zeilen beginnen.
List
Implementiert jetztIEnumerable
, repräsentiert aber die gesamte Sammlung im Speicher. Wenn Sie eine habenIEnumerable
und Sie anrufen.ToList()
, erstellen Sie eine neue Liste mit dem Inhalt der Aufzählung im Speicher.Ihr linq-Ausdruck gibt eine Aufzählung zurück, und der Ausdruck wird standardmäßig ausgeführt, wenn Sie die Verwendung von durchlaufen
foreach
. EineIEnumerable
linq-Anweisung wird ausgeführt, wenn Sie die iterieren.foreach
Sie können sie jedoch zwingen, sie früher zu iterieren.ToList()
.Folgendes meine ich:
quelle
foreach
während Liste sagen Index geht. Wenn ich die Anzahl / Länge vonthing
vorher wissen möchte , hilft IEnumerable nicht, oder?List
undArray
implementierenIEnumerable
. Sie können sichIEnumerable
einen kleinsten gemeinsamen Nenner vorstellen, der sowohl für Speichersammlungen als auch für große Sammlungen funktioniert, die jeweils ein Element erhalten. Wenn Sie anrufen, rufenIEnumerable.Count()
Sie möglicherweise eine schnelle.Length
Eigenschaft auf oder gehen die gesamte Sammlung durch - der Punkt ist, dassIEnumerable
Sie es nicht wissen. Das kann ein Problem sein, aber wenn Sie nur dazu gehen, istforeach
es Ihnen egal - Ihr Code funktioniert mit einemArray
oderDataReader
demselben.Niemand erwähnte einen entscheidenden Unterschied, der ironischerweise auf eine Frage beantwortet wurde, die als Duplikat davon geschlossen wurde.
Siehe Praktischer Unterschied zwischen List und IEnumerable
quelle
Das Wichtigste ist, dass die Abfrage mit Linq nicht sofort ausgewertet wird. Es wird nur als Teil des Durchlaufens des resultierenden
IEnumerable<T>
a ausgeführtforeach
- das ist es, was alle seltsamen Delegierten tun.Im ersten Beispiel wird die Abfrage sofort ausgewertet,
ToList
indem die Abfrageergebnisse aufgerufen und in eine Liste aufgenommen werden.Das zweite Beispiel gibt eine zurück
IEnumerable<T>
, die alle Informationen enthält, die zum späteren Ausführen der Abfrage erforderlich sind.In Bezug auf die Leistung ist die Antwort, dass es darauf ankommt . Wenn die Ergebnisse sofort ausgewertet werden sollen (z. B. wenn Sie die Strukturen, die Sie später abfragen, mutieren oder wenn die Iteration nicht
IEnumerable<T>
lange dauern soll), verwenden Sie eine Liste. Andernfalls verwenden Sie eineIEnumerable<T>
. Standardmäßig sollte die On-Demand-Auswertung im zweiten Beispiel verwendet werden, da diese im Allgemeinen weniger Speicher benötigt, es sei denn, es gibt einen bestimmten Grund, die Ergebnisse in einer Liste zu speichern.quelle
Join
Arbeit - innen und außen sind die beiden Seiten der Verbindung. Machen Sie sich im Allgemeinen keine Sorgen darüber, was tatsächlich imIEnumerables
Code enthalten ist, da dieser sich vollständig von Ihrem eigentlichen Code unterscheidet. Sorgen Sie sich nur um die tatsächliche Ausgabe, wenn Sie darüber iterieren :)Der Vorteil von IEnumerable ist die verzögerte Ausführung (normalerweise mit Datenbanken). Die Abfrage wird erst ausgeführt, wenn Sie die Daten tatsächlich durchlaufen haben. Es ist eine Abfrage, die wartet, bis sie benötigt wird (auch bekannt als verzögertes Laden).
Wenn Sie ToList aufrufen, wird die Abfrage ausgeführt oder "materialisiert", wie ich gerne sagen möchte.
Beide haben Vor- und Nachteile. Wenn Sie ToList aufrufen, können Sie einige Rätsel lösen, wann die Abfrage ausgeführt wird. Wenn Sie sich an IEnumerable halten, haben Sie den Vorteil, dass das Programm erst dann arbeitet, wenn es tatsächlich benötigt wird.
quelle
Ich werde ein missbrauchtes Konzept teilen, in das ich eines Tages geraten bin:
Erwartetes Ergebnis
Tatsächliche Ergebnis
Erläuterung
Wie aus anderen Antworten hervorgeht, wurde die Auswertung des Ergebnisses bis zum Aufruf
ToList
oder ähnlichen Aufrufmethoden verschobenToArray
.Daher kann ich den Code in diesem Fall wie folgt umschreiben:
Spielen Sie herum
https://repl.it/E8Ki/0
quelle
IEnumerable
funktioniert, aber jetzt ist nicht mehr, da ich weiß wie;)Wenn Sie sie nur aufzählen möchten, verwenden Sie die
IEnumerable
.Beachten Sie jedoch, dass das Ändern der aufgezählten Originalsammlung eine gefährliche Operation ist. In diesem Fall sollten Sie dies zuerst tun
ToList
. Dadurch wird für jedes Element im Speicher ein neues Listenelement erstellt, das das Element auflistet,IEnumerable
und es ist daher weniger leistungsfähig, wenn Sie nur einmal auflisten - aber sicherer und manchmal sind dieList
Methoden praktisch (z. B. bei wahlfreiem Zugriff).quelle
IEnumerable
iterieren, bedeutet das erstmalige Erstellen einer Liste (und das Iterieren darüber), dass Sie die Elemente zweimal durchlaufen . Wenn Sie also keine Operationen ausführen möchten, die auf der Liste effizienter sind, bedeutet dies wirklich eine geringere Leistung..ToList()
hilft hier;)Zusätzlich zu allen oben genannten Antworten sind hier meine zwei Cent. Es gibt viele andere Typen als List, die IEnumerable wie ICollection, ArrayList usw. implementieren. Wenn wir also IEnumerable als Parameter einer Methode haben, können wir alle Auflistungstypen an die Funktion übergeben. Das heißt, wir können eine Methode haben, um mit der Abstraktion zu arbeiten, keine spezifische Implementierung.
quelle
Es gibt viele Fälle (z. B. eine unendliche Liste oder eine sehr große Liste), in denen IEnumerable nicht in eine Liste umgewandelt werden kann. Die offensichtlichsten Beispiele sind alle Primzahlen, alle Benutzer von Facebook mit ihren Details oder alle Artikel bei ebay.
Der Unterschied besteht darin, dass "List" -Objekte "genau hier und genau jetzt" gespeichert werden, während "IEnumerable" -Objekte "nur einzeln" funktionieren. Wenn ich also alle Artikel bei ebay durchgehen würde, wäre es etwas, mit dem selbst ein kleiner Computer umgehen kann, aber ".ToList ()" würde mir sicherlich den Speicher ausgehen, egal wie groß mein Computer war. Kein Computer kann für sich genommen so viele Daten enthalten und verarbeiten.
[Bearbeiten] - Unnötig zu sagen - es ist nicht "entweder dies oder das". Oft ist es sinnvoll, sowohl eine Liste als auch eine IEnumerable in derselben Klasse zu verwenden. Kein Computer auf der Welt könnte alle Primzahlen auflisten, da dies per Definition unendlich viel Speicher erfordern würde. Aber man könnte sich leicht ein
class PrimeContainer
vorstellenIEnumerable<long> primes
, das ein enthält , das aus offensichtlichen Gründen auch ein enthältSortedList<long> _primes
. alle bisher berechneten Primzahlen. Die nächste zu prüfende Primzahl würde nur gegen die vorhandenen Primzahlen (bis zur Quadratwurzel) ausgeführt. Auf diese Weise erhalten Sie sowohl Primzahlen einzeln (IEnumerable) als auch eine gute Liste von "Primzahlen bisher", was eine ziemlich gute Annäherung an die gesamte (unendliche) Liste darstellt.quelle