Wann ist jeder von T [], ListT, IEnumerableT zu verwenden?

8

Normalerweise finde ich etwas wie:

%Vor%

und ich finde all diese Verwechslung von Typen / Interface ein wenig verwirrend und es kitzelt meine potentielle Performance-Problem-Antenne (die ich natürlich bis zum erwiesenen Recht ignoriere).

Ist das idiomatische und korrekte C # oder gibt es eine bessere Alternative, um das Hin und Her zu vermeiden, um auf die richtigen Methoden zum Arbeiten mit den Daten zugreifen zu können?

BEARBEITEN: Die Frage ist eigentlich zweifach:

  • Wann ist es richtig, entweder die IEnumerable-Schnittstelle oder ein Array oder eine Liste (oder einen anderen IEnumerable-Implementierungstyp) direkt zu verwenden (wenn Parameter akzeptiert werden)?

  • Sollten Sie frei zwischen IEnumerables (Implementierung unbekannt) und Listen und IEnumerables und Arrays und Arrays und Listen wechseln oder ist das nicht idiomatisch (es gibt bessere Möglichkeiten, dies zu tun) / nicht performant (nicht typischerweise relevant, aber möglicherweise in einigen Fällen) / einfach nur hässlich (nicht mischbar, unlesbar)?

Vinko Vrsalovic 04.08.2010, 23:14
quelle

7 Antworten

7

In Bezug auf die Leistung ...

  • Beim Konvertieren von List in T [] werden alle Daten aus der ursprünglichen Liste in ein neu zugewiesenes Array kopiert.
  • Bei der Konvertierung von T [] in List werden auch alle Daten aus der ursprünglichen Liste in eine neu zugewiesene Liste kopiert.
  • Die Konvertierung von entweder List oder T [] nach IEnumerable beinhaltet Casting, das sind ein paar CPU-Zyklen.
  • Die Konvertierung von IEnumerable in List umfasst das Upcasting, das auch einige CPU-Zyklen umfasst.
  • Die Konvertierung von IEnumerable in T [] beinhaltet auch Upcasting.
  • Sie können IEnumerable nicht an T [] oder List übergeben, es sei denn, es war ein T [] oder eine Liste, um damit zu beginnen. Sie können die ToArray- oder ToList-Funktionen verwenden, aber diese führen auch dazu, dass eine Kopie erstellt wird.
  • Der Zugriff auf alle Werte in der Reihenfolge von Anfang bis Ende in einem T [] wird in einer einfachen Schleife optimiert, um eine einfache Zeigerarithmetik zu verwenden - was sie zu der schnellsten von allen macht.
  • Der Zugriff auf alle Werte in der Reihenfolge vom Anfang bis zum Ende in einer Liste beinhaltet eine Überprüfung jeder Iteration, um sicherzustellen, dass Sie nicht auf einen Wert außerhalb der Grenzen des Arrays zugreifen, und dann den tatsächlichen Zugriff auf den Array-Wert.
  • Der Zugriff auf alle Werte in einem IEnumerable umfasst das Erstellen eines Enumeratorobjekts, das Aufrufen der Funktion Next (), die den Indexzeiger erhöht, und den Aufruf der Current-Eigenschaft, die den tatsächlichen Wert angibt, in der von Ihnen angegebenen Variablen Ihre foreach Aussage. Im Allgemeinen ist das nicht so schlimm wie es klingt.
  • Wenn Sie auf einen beliebigen Wert in einem IEnumerable zugreifen, müssen Sie am Anfang beginnen und Next () so oft aufrufen, wie Sie benötigen, um zu diesem Wert zu gelangen. Im Allgemeinen ist das so schlecht wie es klingt.

In Bezug auf Idiome ...

Im Allgemeinen ist IEnumerable nützlich für öffentliche Eigenschaften, Funktionsparameter und oft für Rückgabewerte - und nur, wenn Sie wissen, dass Sie die Werte der Reihe nach verwenden werden.

Wenn Sie beispielsweise eine Funktion PrintValues ​​hätten, wenn sie als PrintValues ​​(List & lt; T & gt; -Werte) geschrieben wäre, wäre sie nur in der Lage, mit Listenwerten umzugehen, so dass der Benutzer zum Beispiel zuerst konvertieren müsste Sie benutzten ein T []. Ebenso mit, wenn die Funktion PrintValues ​​(T [] Werte) war. Aber wenn es PrintValues ​​(IEnumerable & lt; T & gt; -Werte) wäre, wäre es in der Lage, mit Listen, T [] s, Stapeln, Hashtabellen, Wörterbüchern, Zeichenketten, Mengen usw. umzugehen - jede Sammlung, die IEnumerable implementiert Sammlung.

In Bezug auf den internen Gebrauch ...

  • Verwenden Sie eine Liste nur, wenn Sie nicht sicher sind, wie viele Elemente darin enthalten sein müssen.
  • Verwenden Sie ein T [], wenn Sie wissen, wie viele Elemente darin enthalten sein müssen, aber auf die Werte in einer beliebigen Reihenfolge zugreifen müssen.
  • Bleiben Sie bei IEnumerable, wenn Ihnen das gegeben wurde, und Sie müssen es nur sequenziell verwenden. Viele Funktionen geben IEnumerables zurück. Wenn Sie auf Werte von einem IEnumerable in einer beliebigen Reihenfolge zugreifen müssen, verwenden Sie ToArray ().

Beachten Sie auch, dass sich das Casting von der Verwendung von ToArray () oder ToList () unterscheidet. Letzteres beinhaltet das Kopieren der Werte, was in der Tat ein Performance- und Speicher-Hit ist, wenn Sie viele Elemente haben. Der erste ist einfach zu sagen: "Ein Hund ist ein Tier, so wie jedes Tier, kann es essen" (niedergeschlagen) oder "Dieses Tier ist zufällig ein Hund, so kann es bellen" (upcast). Ebenso sind alle Listen und T [] s IEnumerables, aber nur einige IEnumerables sind Listen oder T [] s.

    
Rei Miyasaka 04.08.2010, 23:57
quelle
7

Eine gute Faustregel ist, dass immer IEnumerable verwendet (wenn Sie Ihre Variablen / Methodenparameter / Methoden Rückgabetypen / Eigenschaften / etc. deklarieren), es sei denn, Sie haben einen guten Grund, dies nicht zu tun. Bei weitem die meisten Typ-kompatibel mit anderen (vor allem Erweiterung) Methoden.

    
Kirk Woll 04.08.2010 23:22
quelle
3

Nun, Sie haben zwei Äpfel und eine Orange, die Sie vergleichen.

Die zwei Äpfel sind das Array und die Liste.

  • Ein Array in C # ist ein Array im C-Stil, in das eine Garbage-Collection integriert ist. Die Vorteile bei der Verwendung sind, dass sie sehr wenig Overhead haben, vorausgesetzt, Sie müssen die Dinge nicht verschieben. Das Schlimme ist, dass sie nicht so effizient sind, wenn du Dinge hinzufügst, Dinge entfernst und das Array auf andere Weise veränderst, während Speicher herumgeschoben wird.

  • Eine Liste ist ein dynamisches Array im C # -Stil (ähnlich der Klasse Vektor & lt; & gt; in C ++). Es gibt mehr Overhead, aber sie sind effizienter, wenn Sie Dinge viel bewegen müssen, da sie nicht versuchen, die Speichernutzung zusammenhängend zu halten.

Der beste Vergleich, den ich geben könnte, ist zu sagen, dass Arrays Listen sind, wie Strings zu StringBuilders sind.

Die Orange ist "IEnumerable". Dies ist kein Datentyp, sondern eine Schnittstelle. Wenn eine Klasse die IEnumerable-Schnittstelle implementiert, kann dieses Objekt in einer foreach () - Schleife verwendet werden.

Wenn Sie die Liste zurückgeben (wie in Ihrem Beispiel), wurde die Liste nicht in eine IEnumerable konvertiert. Eine Liste ist bereits ein IEnumerable-Objekt.

EDIT: Wann zwischen den zwei zu konvertieren:

Es hängt von der Anwendung ab. Es gibt sehr wenig, was mit einem Array getan werden kann, das mit einer Liste nicht möglich ist, daher würde ich die Liste generell empfehlen. Wahrscheinlich ist es das Beste, eine Designentscheidung zu treffen, dass Sie das eine oder das andere verwenden werden, so dass Sie nicht zwischen den beiden wechseln müssen. Wenn Sie sich auf eine externe Bibliothek verlassen, sollten Sie sie abstrahieren, um eine konsistente Nutzung zu gewährleisten.

Hoffe, das löscht ein bisschen vom Nebel.

    
riwalk 04.08.2010 23:25
quelle
1

Sieht so aus, als ob das Problem darin besteht, dass Sie sich nicht die Mühe gemacht haben, zu lernen, wie man ein Array sucht. Hinweis: Array.IndexOf oder Array.BinarySearch abhängig davon, ob das Array sortiert ist.

Sie haben Recht, dass das Konvertieren in eine Liste eine schlechte Idee ist: Sie verschwendet Speicherplatz und Zeit und macht den Code weniger lesbar. Blindes Upcasting auf IEnumerable verlangsamt den Prozess und verhindert auch die Verwendung bestimmter Algorithmen (z. B. binäre Suche).

    
Ben Voigt 04.08.2010 23:29
quelle
0

Ich versuche zu vermeiden, schnell zwischen Datentypen zu springen, wenn es vermieden werden kann.

Es muss der Fall sein, dass jede Situation, die der von Ihnen beschriebenen ähnlich ist, ausreichend verschieden ist, um eine dogmatische Regel bezüglich der Umwandlung Ihrer Typen zu verhindern; Es empfiehlt sich jedoch, eine Datenstruktur auszuwählen, die die von Ihnen benötigte Schnittstelle bestmöglich bereitstellt, ohne Elemente unnötig in neue Datenstrukturen kopieren zu müssen.

    
kbrimington 04.08.2010 23:21
quelle
0

Wann soll was verwendet werden?

Ich würde vorschlagen, den spezifischsten Typ zurückzugeben und den flexibelsten Typ zu verwenden.

So:

%Vor%

Auf diese Weise können Sie Methoden auf List< T > aufrufen, unabhängig davon, was Sie von der Methode zurückerhalten, zusätzlich zur Behandlung als IEnumerable. Verwenden Sie die Eingaben so flexibel wie möglich, damit Sie den Benutzern Ihrer Methode nicht vorschreiben, welche Art von Sammlung erstellt werden soll.

    
Arjan Einbu 05.08.2010 00:08
quelle
-2

Sie haben Recht, die "Performance-Problem" -Antennen zu ignorieren, bis Sie tatsächlich ein Leistungsproblem haben. Die meisten Leistungsprobleme kommen von zu vielen I / O oder zu viel Sperren oder einer von ihnen falsch, und keiner von diesen trifft auf diese Frage zu.

Mein allgemeiner Ansatz ist:

  1. Verwenden Sie T [] für Informationen zu "statisch" oder "Schnappschuss". Verwenden Sie für Dinge, in denen aufrufen .Add () sowieso keinen Sinn macht, und Sie die zusätzlichen Methoden nicht benötigen List & lt; T & gt; gibt dir.
  2. Akzeptiere IEnumerable & lt; T & gt; wenn es Ihnen nicht wirklich wichtig ist, was Ihnen gegeben wurde und Sie keine Konstantenzeit .Length / .Count benötigen.
  3. Geben Sie nur IEnumerable & lt; T & gt; wenn Sie einfache Manipulationen an einem Input IEnumerable & lt; T & gt; oder wenn Sie speziell die Ertragssyntax verwenden möchten, um Ihre Arbeit träge zu erledigen.
  4. In allen anderen Fällen verwenden Sie List & lt; T & gt ;. Es ist einfach zu flexibel.

Korollar zu # 4: keine Angst vor ToList () haben. ToList () ist dein Freund. Es erzwingt das IEnumerable & lt; T & gt; dann richtig bewerten (nützlich, wenn Sie mehrere where-Klauseln übereinander legen). Machen Sie sich nicht verrückt damit, aber fühlen Sie sich frei, es zu nennen, sobald Sie Ihre vollständige where-Klausel aufgebaut haben, bevor Sie die foreach darüber (oder dergleichen) machen.

Das ist natürlich nur eine grobe Richtlinie. Versuchen Sie einfach, das gleiche Muster in der gleichen Codebasis zu verwenden - Code-Stile, die herumspringen, machen es für Wartungs-Programmierer schwieriger, in Ihre Stimmung zu kommen.

    
Jonathan Rupp 04.08.2010 23:29
quelle

Tags und Links