858 Stimmen

IEnumerable vs. Liste - Was ist zu verwenden? Wie funktionieren sie?

Ich habe einige Zweifel darüber, wie Enumerators arbeiten, und LINQ. Betrachten Sie diese beiden einfachen Selects:

List<Animal> sel = (from animal in Animals 
                    join race in Species
                    on animal.SpeciesKey equals race.SpeciesKey
                    select animal).Distinct().ToList();

ou

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 dies:

foreach (Animal animal in sel) { /*do stuff*/ }
  1. Ich habe festgestellt, dass ich bei der Verwendung von IEnumerable Wenn ich debugge und "sel" inspiziere, was in diesem Fall die IEnumerable ist, hat sie einige interessante Mitglieder: "inner", "outer", "innerKeySelector" und "outerKeySelector", die letzten beiden scheinen Delegierte zu sein. Das "inner"-Mitglied enthält keine "Animal"-Instanzen, sondern eher "Species"-Instanzen, was für mich sehr seltsam war. Das "äußere" Element enthält "Animal"-Instanzen. Ich nehme an, dass die beiden Delegierten bestimmen, was hineinkommt und was herauskommt?

  2. Ich habe festgestellt, dass, wenn ich "Distinct" verwende, die "innere" 6 Elemente enthält (dies ist falsch, da nur 2 Distinct sind), aber die "äußere" enthält die richtigen Werte. Wieder, wahrscheinlich die delegierten Methoden bestimmen dies, aber dies ist ein bisschen mehr als ich über IEnumerable wissen.

  3. Und vor allem: Welche der beiden Optionen ist leistungsmäßig die beste?

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

Oder vielleicht direkt den Enumerator verwenden?

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

50voto

Matt Sherman Punkte 8013

Der Vorteil von IEnumerable ist die verzögerte Ausführung (normalerweise bei Datenbanken). Die Abfrage wird erst ausgeführt, wenn Sie tatsächlich eine Schleife durch die Daten ziehen. 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 zu sagen pflege.

Beide haben ihre Vor- und Nachteile. Wenn Sie ToList aufrufen, können Sie das Geheimnis lüften, wann die Abfrage ausgeführt wird. Wenn Sie sich an IEnumerable halten, haben Sie den Vorteil, dass das Programm keine Arbeit leistet, bis es tatsächlich benötigt wird.

44voto

amd Punkte 19366

Ich möchte Ihnen ein missbrauchtes Konzept vorstellen, auf das ich eines Tages gestoßen 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ächliches Ergebnis

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

Erläuterung

Wie bei anderen Antworten wurde die Auswertung des Ergebnisses bis zum Aufruf von ToList oder ähnliche Aufrufmethoden, zum Beispiel ToArray .

Ich kann den Code in diesem Fall also umschreiben als:

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() );

Spielrunde

https://repl.it/E8Ki/0

16voto

Daren Thomas Punkte 64742

Wenn Sie sie nur aufzählen wollen, verwenden Sie die IEnumerable .

Beachten Sie jedoch, dass das Ändern der ursprünglichen Sammlung, die aufgezählt wird, eine gefährliche Operation ist - in diesem Fall müssen Sie die ToList zuerst. Dadurch wird für jedes Element im Speicher ein neues Listenelement erstellt, das die folgenden Elemente aufzählt IEnumerable und ist daher weniger performant, wenn man nur einmal aufzählt - aber sicherer und manchmal die List Methoden sind praktisch (z. B. beim wahlfreien Zugriff).

7voto

Ananth Punkte 9922

Zusätzlich zu all den Antworten, die oben gepostet wurden, hier noch meine Meinung dazu. 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 jede Art von Sammlung an die Funktion übergeben. D.h. wir können eine Methode haben, die mit der Abstraktion arbeitet und nicht mit einer spezifischen Implementierung.

7voto

LongChalk Punkte 650

Der Nachteil von IEnumerable (eine aufgeschobene Ausführung) ist, dass bis zum Aufrufen der .ToList() die Liste kann sich möglicherweise ändern. Für ein wirklich einfaches Beispiel würde dies funktionieren

var persons;
using (MyEntities db = new MyEntities()) {
    persons = db.Persons.ToList(); // It's mine now. In the memory
}
// do what you want with the list of persons;

und das würde nicht funktionieren

IEnumerable<Person> persons;
 using (MyEntities db = new MyEntities()) {
     persons = db.Persons; // nothing is brought until you use it;
 }

persons = persons.ToList();  // trying to use it...
// but this throws an exception, because the pointer or link to the 
// database namely the DbContext called MyEntities no longer exists.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X