it-swarm.com.de

IEnumerable vs List - Was ist zu verwenden? Wie arbeiten Sie?

Ich habe einige Zweifel darüber, wie Enumerators und LINQ funktionieren. Betrachten Sie diese zwei einfachen Auswahlen:

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*/ }
  1. Ich habe festgestellt, dass wenn ich IEnumerable verwende, wenn ich "sel", das in diesem Fall die IEnumerable ist, debugge und inspiziere, es einige interessante Mitglieder hat: "inner", "outer", "innerKeySelector" und "outerKeySelector" scheinen diese letzten 2 Delegierten zu sein. Das "innere" Element enthält keine "Animal" -Instanzen, sondern "Species" -Instanzen, was für mich sehr seltsam war. Das "äußere" Element enthält "Tier" -Instanzen. Ich nehme an, dass die beiden Delegierten bestimmen, was rein und was raus geht.

  2. Ich habe festgestellt, dass bei Verwendung von "Distinct" das "innere" 6 Elemente enthält (dies ist falsch, da nur 2 Distinct sind), das "äußere" jedoch die korrekten Werte enthält. Auch hier bestimmen wahrscheinlich die delegierten Methoden dies, aber dies ist etwas mehr, als ich über IEnumerable weiß.

  3. Welche der beiden Optionen ist am leistungsstärksten?

Die Konvertierung der bösen Liste über .ToList()?

Oder verwenden Sie den Enumerator direkt?

Wenn Sie können, erklären Sie bitte auch ein wenig oder werfen Sie einige Links, die diese Verwendung von IEnumerable erklären.

616
Axonn

IEnumerable beschreibt das Verhalten, während List eine Implementierung dieses Verhaltens ist. Wenn Sie IEnumerable verwenden, können Sie dem Compiler die Möglichkeit geben, die Arbeit auf einen späteren Zeitpunkt zu verschieben und möglicherweise auf diese Weise zu optimieren. Wenn Sie ToList () verwenden, erzwingen Sie, dass der Compiler die Ergebnisse sofort überprüft.

Immer, wenn ich LINQ-Ausdrücke "stapele", verwende ich IEnumerable, da ich LINQ die Möglichkeit gebe, die Auswertung zu verschieben und möglicherweise das Programm zu optimieren, indem ich nur das Verhalten spezifiziere. Erinnern Sie sich, wie LINQ die SQL zum Abfragen der Datenbank erst generiert, wenn Sie sie aufgelistet haben? Bedenken Sie:

public IEnumerable<Animals> AllSpotted()
{
    return from a in Zoo.Animals
           where a.coat.HasSpots == true
           select a;
}

public IEnumerable<Animals> Feline(IEnumerable<Animals> sample)
{
    return from a in sample
           where a.race.Family == "Felidae"
           select a;
}

public IEnumerable<Animals> Canine(IEnumerable<Animals> sample)
{
    return from a in sample
           where a.race.Family == "Canidae"
           select a;
}

Jetzt haben Sie eine Methode, mit der Sie ein erstes Sample auswählen ("AllSpotted"), sowie einige Filter. Nun können Sie dies tun:

var Leopards = Feline(AllSpotted());
var Hyenas = Canine(AllSpotted());

Ist es also schneller, List über IEnumerable zu verwenden? Nur wenn Sie verhindern möchten, dass eine Abfrage mehrmals ausgeführt wird. Aber ist es insgesamt besser? Im obigen Beispiel werden Leoparden und Hyänen in jeweils einzelne SQL-Abfragen konvertiert, und die Datenbank gibt nur die Zeilen zurück, die relevant sind. Wenn wir jedoch eine Liste von AllSpotted() zurückgegeben hätten, könnte sie langsamer ausgeführt werden, da die Datenbank weitaus mehr Daten zurückgeben könnte, als tatsächlich benötigt werden, und wir verschwenden Zyklen mit der Filterung im Client.

In einem Programm ist es möglicherweise besser, die Konvertierung Ihrer Abfrage in eine Liste bis zum Ende zu verschieben. Wenn ich also Leoparden und Hyänen mehrmals aufzählen möchte, würde ich Folgendes tun:

List<Animals> Leopards = Feline(AllSpotted()).ToList();
List<Animals> Hyenas = Canine(AllSpotted()).ToList();
679
Chris Wenham

Es gibt einen sehr guten Artikel von: Claudio Bernasconis TechBlog hier: Verwendung von IEnumerable, ICollection, IList und List

Hier einige grundlegende Punkte zu Szenarien und Funktionen:

enter image description hereenter image description here

139

Mit einer Klasse, die IEnumerable implementiert, können Sie die foreach -Syntax verwenden.

Grundsätzlich gibt es eine Methode, um das nächste Element in der Sammlung abzurufen. Es ist nicht erforderlich, dass sich die gesamte Sammlung im Speicher befindet, und es ist nicht bekannt, wie viele Elemente sich darin befinden. foreach ruft nur das nächste Element ab, bis es leer ist.

Dies kann unter bestimmten Umständen sehr nützlich sein, z. B. wenn Sie in einer umfangreichen Datenbanktabelle nicht das gesamte Objekt in den Speicher kopieren möchten, bevor Sie mit der Verarbeitung der Zeilen beginnen.

Jetzt implementiert ListIEnumerable, repräsentiert aber die gesamte Sammlung im Speicher. Wenn Sie ein IEnumerable haben und .ToList() aufrufen, 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 mit foreach iterieren. Eine IEnumerable linq-Anweisung wird ausgeführt, wenn Sie foreach durchlaufen, Sie können sie jedoch mit .ToList() zu einer schnelleren Iteration zwingen.

Folgendes meine ich:

var things = 
    from item in BigDatabaseCall()
    where ....
    select item;

// this will iterate through the entire linq statement:
int count = things.Count();

// this will stop after iterating the first one, but will execute the linq again
bool hasAnyRecs = things.Any();

// this will execute the linq statement *again*
foreach( var thing in things ) ...

// this will copy the results to a list in memory
var list = things.ToList()

// this won't iterate through again, the list knows how many items are in it
int count2 = list.Count();

// this won't execute the linq statement - we have it copied to the list
foreach( var thing in list ) ...
124
Keith

Niemand erwähnte einen entscheidenden Unterschied, ironischerweise beantwortet auf eine Frage, die als ein Duplikat davon geschlossen wurde.

IEnumerable ist schreibgeschützt und List nicht.

Siehe Praktischer Unterschied zwischen List und IEnumerable

78
CAD bloke

Das Wichtigste ist, dass die Abfrage mit Linq nicht sofort ausgewertet wird. Es wird nur als Teil der Iteration durch den resultierenden IEnumerable<T> in einem foreach ausgeführt - das ist, was alle seltsamen Delegierten tun.

Das erste Beispiel wertet die Abfrage also sofort aus, indem es ToList aufruft und die Abfrageergebnisse in eine Liste schreibt.
Das zweite Beispiel gibt einen IEnumerable<T> zurück, der alle Informationen enthält, die zum späteren Ausführen der Abfrage erforderlich sind.

In Bezug auf die Leistung lautet die Antwort es kommt darauf an. Wenn die Ergebnisse sofort ausgewertet werden müssen (z. B. wenn Sie die Strukturen, die Sie später abfragen, mutieren oder wenn die Iteration über den IEnumerable<T> nicht lange dauern soll), verwenden Sie a Liste. Verwenden Sie andernfalls einen IEnumerable<T>. Die Standardeinstellung sollte die On-Demand-Auswertung im zweiten Beispiel sein, da diese im Allgemeinen weniger Speicher benötigt, es sei denn, es gibt einen bestimmten Grund, die Ergebnisse in einer Liste zu speichern.

65
thecoop

Der Vorteil von IEnumerable ist die verzögerte Ausführung (normalerweise bei 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 Lazy Loading).

Wenn Sie ToList aufrufen, wird die Abfrage ausgeführt oder "materialisiert", wie ich sagen möchte.

Beide haben Vor- und Nachteile. Wenn Sie ToList aufrufen, können Sie einige Rätsel beseitigen, wann die Abfrage ausgeführt wird. Wenn Sie sich an IEnumerable halten, haben Sie den Vorteil, dass das Programm erst dann funktioniert, wenn es tatsächlich benötigt wird.

38
Matt Sherman

Ich werde ein missbrauchtes Konzept teilen, in das ich eines Tages verfallen bin:

var names = new List<string> {"mercedes", "mazda", "bmw", "fiat", "ferrari"};

var startingWith_M = names.Where(x => x.StartsWith("m"));

var startingWith_F = names.Where(x => x.StartsWith("f"));


// updating existing list
names[0] = "ford";

// Guess what should be printed before continuing
print( startingWith_M.ToList() );
print( startingWith_F.ToList() );

Erwartetes Ergebnis

// I was expecting    
print( startingWith_M.ToList() ); // mercedes, mazda
print( startingWith_F.ToList() ); // fiat, ferrari

Tatsächliche Ergebnis

// what printed actualy   
print( startingWith_M.ToList() ); // mazda
print( startingWith_F.ToList() ); // ford, fiat, ferrari

Erläuterung

Gemäß anderen Antworten wurde die Auswertung des Ergebnisses verschoben, bis ToList oder ähnliche Aufrufmethoden aufgerufen wurden, zum Beispiel ToArray.

Daher kann ich den Code in diesem Fall wie folgt umschreiben:

var names = new List<string> {"mercedes", "mazda", "bmw", "fiat", "ferrari"};

// updating existing list
names[0] = "ford";

// before calling ToList directly
var startingWith_M = names.Where(x => x.StartsWith("m"));

var startingWith_F = names.Where(x => x.StartsWith("f"));

print( startingWith_M.ToList() );
print( startingWith_F.ToList() );

In der Nähe spielen

https://repl.it/E8Ki/

18
amd

Wenn Sie sie nur aufzählen möchten, verwenden Sie IEnumerable.

Beachten Sie jedoch, dass das Ändern der zu zählenden Originalsammlung eine gefährliche Operation ist. In diesem Fall möchten Sie zunächst ToList. Dadurch wird für jedes Element im Speicher ein neues Listenelement erstellt, das IEnumerable auflistet. Wenn Sie nur einmal auflisten, ist dies weniger performant - aber sicherer und manchmal sind die List -Methoden praktisch (z. B. beim wahlfreien Zugriff) ).

15
Daren Thomas

Zusätzlich zu all den 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 der Funktion beliebige Auflistungstypen übergeben. Das heißt, wir können eine Methode haben, um mit der Abstraktion zu arbeiten, nicht mit einer bestimmten Implementierung.

5
Ananth

Es gibt viele Fälle (wie 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 Facebook-Nutzer mit ihren Details oder alle Artikel bei ebay.

Der Unterschied besteht darin, dass "Listen" -Objekte "genau hier und jetzt" gespeichert werden, während "IEnumerable" -Objekte "nur nacheinander" funktionieren. Wenn ich also alle Artikel bei ebay durchgehen würde, wäre einer nach dem anderen etwas, das sogar ein kleiner Computer handhaben kann, aber ".ToList ()" würde mir mit Sicherheit den Speicher entziehen, 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 Speicherplatz erfordern würde. Man könnte sich aber leicht einen class PrimeContainer vorstellen, der einen IEnumerable<long> primes enthält, der aus offensichtlichen Gründen auch einen SortedList<long> _primes enthält. alle bisher berechneten Primzahlen. Die nächste zu überprüfende Primzahl würde nur gegen die vorhandenen Primzahlen (bis zur Quadratwurzel) laufen. Auf diese Weise erhalten Sie sowohl Primzahlen nacheinander (IEnumerable) als auch eine gute Liste von "Primzahlen", was eine ziemlich gute Annäherung an die gesamte (unendliche) Liste darstellt.

1
LongChalk