2012-03-25 13 views
3

Meine Frage ist wirklich einfach. Wann sollte ich List, IEnumerable und ArrayList verwenden?Wann verwenden Sie die Liste <T>, IEnumerable <T> und ArrayList

Hier ist mein Szenario. Ich arbeite in einer Web-App mit LINQ. Die Informationen werden als IEnumerable zurückgegeben:

IEnumerable<Inventory> result = from Inventory i in db where.... 

Ich bin nicht sicher, wie IEnumerable funktioniert, aber jeder Betrieb nimmt viel Zeit auszuführen. Genauer gesagt, result.Count(), result.ElementAt (i), result.ToList usw. benötigt jede Operation eine beträchtliche Zeit.

Also habe ich mich gefragt, ob ich dies als eine Liste behandeln sollte, indem ich result.ToList statt mit der IEnumerable-Variable arbeiten.

Danke!

+0

Welchen LINQ-Provider verwenden Sie (d. H. Wo kommen die Daten her?)? Welche Größe hat die Liste? – Oded

+0

Aber 'result.ToList' würde in diesem Fall mit IEnumerable arbeiten. –

+0

Was @ L.B versucht zu sagen ist, dass die Langsamkeit, die Sie sehen, nicht wegen der Verwendung von IEnumeralbe ist. – Oded

Antwort

6

Wenn ich verstehe, was du tust richtig, Sie haben eine Abfrage wie from Inventory i in db select i und dann tun Sie mehrere Operationen auf dem Ergebnis:

var count = result.Count(); 
var fifth = result.ElementAt(5); 
var allItems = result.ToList(); 

Betrachten wir nun, was passiert, wenn Sie die Abfrage als verschiedene Typen haben:

  • IQueryable<T>

    var result = from Inventory i in db select i; 
    IQueryable<Inventory> result = from Inventory i in db select i; 
    

    Die beiden obigen Zeilen sind identisch. Sie gehen nicht wirklich zur Datenbank, sie erstellen nur eine Repräsentation der Abfrage.Wenn Sie dies haben, Count() führt eine SQL-Abfrage wie SELECT COUNT(*) FROM Inventory, ElementAt(5) wird eine andere Abfrage ausführen, die nur das fünfte Element in der Tabelle und ToList() wird etwas wie SELECT * FROM Inventory ausführen, aber das ist, was wir hier wollen.

  • IEnumerable<T>

    IEnumerable<Inventory> result = from Inventory i in db select i; 
    

    dies wieder tun die Datenbank nicht geht, schafft es nur eine Darstellung der Abfrage. Aber es ist eine Repräsentation, die die für IQueryable<T> spezifischen Methoden nicht verwenden kann, also wird jede LINQ-Operation die Auflistung auflisten, die eine SQL-Abfrage wie SELECT * FROM Inventory ausführen wird.

    Also, für das Beispiel: Count() wird die Abfrage SELECT * … nur ausführen, um die Elemente im Ergebnis zu zählen. ElementAt(5) wird die gesamte Abfrage erneut ausführen, nur um alle Elemente außer dem fünften wegzuwerfen. Und ToList() wird die Abfrage noch einmal ausführen.

  • List<T>

    List<Inventory> result = (from Inventory i in db select i).ToList(); 
    

    ausführen dies tatsächlich die SELECT * FROM Inventory Abfrage sofort und einmal. Alle Operationen, die Sie mit result ausführen, berühren die Datenbank nicht, sie werden im Speicher ausgeführt.

Was sollten Sie davon wegnehmen? Zuerst niemals IEnumerable<T> als den Typ der Datenbankabfrage. Es hat eine schreckliche Leistung.

Wenn Sie mehrere verschiedene Operationen am Ergebnis vornehmen möchten, ist die Verwendung der IQueryable<T> die beste Lösung.

Wenn Sie trotzdem das ganze Ergebnis abrufen möchten, verwenden Sie ToList() (oder ToArray()) so schnell wie möglich und dann arbeiten mit der List<T> führt.

2

Ihre Abfrage wird nur ausgeführt, wenn Sie ToList() oder eine andere ähnliche Methode aufrufen.

Dies wird Deffered Execution genannt.

Verwenden Sie IEnumerable wann immer es für Ihre result möglich ist. Leistung der Ausführung LINQ hängt nicht davon ab, was Sie für result verwenden, denn am Ende wird es sowieso als IEnumerable behandelt.

Die LINQ-Leistung hängt jedoch von den zugrunde liegenden Daten ab.

[MIT DETAILS EDITED WURDE]

+1

Das ist falsch. Die Ausführung einer LINQ-Abfrage in der Datenbank kann sehr davon abhängen, ob Sie 'IQueryable ' (effiziente SQL-Abfrage), 'IEnumerable ' (möglicherweise sehr ineffiziente SQL-Abfrage) oder 'List ' (durch einmaliges Aufrufen von 'ToList()' verwenden). möglicherweise einmal sehr ineffizient, danach schnell). – svick

+0

@Andrriy Ich habe meinen Code debugged und die Leistung von LINQ ist OK, das Problem besteht beim Zugriff auf Elemente in meiner IEnumerable-Variable oder andere Methoden. – Gonzalo

+1

Ja, aber es hängt von den zugrunde liegenden Daten ab, aus denen die Abfrage erfolgt. Frage ist über Ergebnisvariable, wie ich verstanden habe. –

0

Wenn Sie einen Linq Ausdruck gegen einen Linq-Abfrage-Provider verwenden, wird das Ergebnis ein ein IQueryable<T>, die eine Erweiterung von IEnumerable<T> ist.

Jedes Mal, wenn Sie über eine IQueryable<T> iterieren, führt der Linq-Abfrageanbieter die Abfrage für die zugrunde liegende Datenquelle aus. Wenn Sie das Ergebnis mehr als einmal durchlaufen möchten, kann es daher effizienter sein, es zuerst in eine Liste zu konvertieren (.ToList()).

Beachten Sie, dass Sie beim Konvertieren Ihres Ergebnisses in eine Liste die tatsächlichen Mitglieder von List<T> anstelle der Erweiterungsmethoden IEnumerable<T> verwenden sollten. Zum Beispiel werden list.ElementAt(i) und list.Count() beide in O(n) Zeit ausgeführt, während list[i] und list.Count in konstanter Zeit ausgeführt werden.

4

Verwenden Sie niemals ArrayList. ArrayList wird zur Kompatibilität mit pre.NET 2.0 beibehalten. Es entspricht List<object>, und es gibt keinen Grund, die generischen Typen in keiner normalen Situation zu verwenden.

Aus Ihrem Codebeispiel geht hervor, dass Sie LINQ to SQL oder ein ähnliches Framework zum Abrufen von Daten aus der Datenbank verwenden. In diesem Fall bringt die select-Anweisung selbst nicht die Daten, sondern nur die Abfrage. Wenn Sie eine Methode wie Count() oder ToList() aufrufen, ruft sie die Daten ab - weshalb sie langsam erscheint. Es ist nicht langsamer, es ist nur das faule Laden in Aktion.

Der Vorteil von IEnumerable besteht darin, dass Sie nicht alle Daten gleichzeitig laden müssen. Wenn Sie nur eine bestimmte where-Klausel abfragen oder Take (1) aufrufen, um das erste Element abzurufen, sollte der LINQ-Provider intelligent genug sein, nur die erforderlichen Elemente aus der Datenbank abzurufen. Wenn Sie jedoch Count() oder ToList() aufrufen, muss das gesamte Dataset abgerufen werden.Wenn Sie diese Art von Informationen benötigen, sollten Sie wahrscheinlich ToList oder ToArray anrufen und den Rest Ihrer Arbeit an der In-Memory-Liste erledigen, so dass Sie die DB nicht noch einmal treffen müssen.

1

Der Unterschied zwischen der Verwendung eines IEnumerable oder eines IList ist eigentlich ziemlich einfach (auf der Oberfläche).

Sie sollten sich den von beiden Schnittstellen definierten Vertrag ansehen. Ein IEnumerable ermöglicht Ihnen einfach, über eine Sequenz aufzuzählen. Mit anderen Worten, die einzige Möglichkeit, auf Daten zuzugreifen, ist die Verwendung eines Enumerators, typischerweise in einer foreach-Schleife. So eine naive Umsetzung der Zählfunktion wäre so etwas wie:

public static int Count(this IEnumerable<T> source) { 
    int count = 0; 
    foreach(var item in myEnumerable) 
    { 
     count++; 
    } 
    return count; 
} 

Das bedeutet, dass die Zeit benötigt, um die Anzahl der Elemente in Ihrem enumerable berechnen wird linear mit der Anzahl der Elemente zu erhöhen. Da dies intern nicht gespeichert wird, müssen Sie diese Schleife jedes Mal durchführen, wenn Sie eine Zählung wünschen.

Ein IList weist bereits eine Count-Eigenschaft auf. Dies ist Teil des Vertrags. Um Count() zu implementieren, würden Sie einfach einen Aufruf an die Count-Eigenschaft übergeben. Dies dauert die gleiche Zeit unabhängig von der Anzahl der Elemente.

Eine einfache Möglichkeit, darüber nachzudenken ist (vor allem mit Linq), ein IEnumerable als eine Spezifikation der Elemente, die Sie benötigen, zu denken. Solange Sie nicht auf die Daten zugreifen, kostet es Ihnen kaum Zeit zu bauen. Sobald Sie mit dem Aufzählen beginnen (alles, was im Grunde etwas anderes als ein IEnumerable zurückgibt), wird der Code ausgeführt und es könnte einige Zeit dauern.

Was ich normalerweise tun möchte, ist die Linq Ausführung im Controller zu halten. Also mache ich meine Abfrage-Erstellung, und dann ToList oder ToArray, bevor Sie es an die Ansicht senden. Der Grund ist ganz einfach: Wenn ich mehr tun muss, als nur auf die Daten in der Ansicht zuzugreifen, bedeutet das, dass ich meiner Meinung nach zu viel mache. Ich bin jetzt gezwungen, diese Logik auf meine Controller-Aktion zu übertragen und meine Ansichten so sauber wie möglich zu halten.

0

Verwendung Generisch Listen/IEnumerable wann immer möglich.

Vermeiden Sie ArrayList. Dies kann zum Einbetten von Werttypen und zum Umwandeln von Referenztypen führen. IEnumerable ist das gleiche - am besten vermieden werden, es sei denn, Sie beschäftigen sich mit Objekten.

IEnumerable<T> zeigt sehr schöne Kovarianz, Kontravarianz Features. Aber es zeigt delayed execution, was ein Fluch ist genauso wie Segen.

List<T> ist besser für den internen Gebrauch bei der Belichtung Schnittstellen wie IEnumerable<T>. List<T> unterstützt keine Kontravarianz.

0

Die Antwort auf die zu verwenden ist "es kommt darauf an, aber meistens verwenden Sie Liste".

Basierend auf dem vollständigen Inhalt Ihrer Frage (lange Verzögerungen mit .Count() und anderen Methoden), sollten Sie zuerst eine toList() auf die Ergebnisse der Abfrage, dann verwenden Sie diese für jeden weiteren Zugriff.

Hier ist warum. Das IEnumerable ist so ziemlich eine Abfrage. Da die abgefragten Daten zwischen den Abläufen der Abfrage geändert werden können, führt ein einzelner Methodenaufruf bei IEnumerable zu einer anderen Datenbanksuche.

Also jedes Mal, wenn Sie .Count() aufrufen, muss jemand zur Datenbank gehen und eine Anzahl aller Objekte erhalten, die Ihrer Anfrage entsprechen. Jedes Mal, wenn Sie ein ElementAt (x) ausführen, muss sich jemand, selbst wenn sich x nicht ändert, immer noch durch die Datenbank gehen und alles abrufen, weil IEnumerable nicht davon ausgehen kann, dass sich die Daten nicht geändert haben.

Auf der anderen Seite, wenn Sie eine Momentaufnahme Ihrer Abfrage mit List erhalten haben, dann ist es ziemlich schnell, Count zu erhalten oder auf zufällige Elemente zuzugreifen.

Also, was zu verwenden - es kommt darauf an.Wenn Sie jedes Mal, wenn Sie auf IEnumerable zugreifen, wissen müssen, was sich in der Datenbank (oder welcher Datenquelle) JETZT befindet, müssen Sie IEnumerable verwenden. Wenn Sie nur daran interessiert sind, was zum Zeitpunkt der Ausführung der ersten Abfrage oder der Ausführung einer Operation über eine konsistente (und/oder statische) Datenquelle vorhanden war, verwenden Sie Liste. Du würdest immer noch einen Zeitsprung bei deinem ersten Zugriff machen, aber alles andere wird schnell sein.

Verwandte Themen