Möglichkeiten zum Erstellen einer konstanten IEnumerableTSomeType ...?

8

Vielleicht ist das eine dumme Frage ... Aber was ist die beste (performance- und memory-weise) Art, eine Konstante IEnumerable<TSomeType> ... zu erstellen?

Wenn es nicht möglich ist, "den besten" Weg zu definieren, welche sind meine Optionen? Was ist Ihrer Meinung nach, denken Sie, dass es den am besten geeigneten Weg dafür gibt?

Zum Beispiel:

  • var enumerable = (IEnumerable<TSomeType>) new List<TSomeType> { Value1, Value2, Value3 };
  • var enumerable = (IEnumerable<TSomeType>) new TSomeType[] { Value1, Value2, Value3 };
  • (einige andere Optionen; zum Beispiel eine Linq Select).

Bitte beachten Sie, dass Speicher und Leistung sind ein Problem hier - wir sprechen über eine wirklich eingeschränkte Umgebung (ein kleines Gerät mit .NET installiert).

Vielen Dank im Voraus.

    
rsenna 03.11.2010, 20:37
quelle

5 Antworten

9

Nun, weder List<T> noch Arrays sind unveränderlich, also sind sie out, wenn Sie wirklich nach der Unveränderlichkeit suchen - der Aufrufer könnte das Ergebnis darstellen und dann modifizieren.

Sie könnten eine List<T> erstellen und diese in eine ReadOnlyCollection<T> umbrechen. Wenn nichts mehr eine Referenz auf die ursprüngliche Liste hat, dann ist es effektiv unveränderlich, ohne Reflexion.

Wenn Sie sich nicht wirklich für die Unveränderlichkeit interessieren - d. h. wenn Sie dem ganzen Code vertrauen, damit er sich nicht damit herumschlägt - dann wird ein Array höchstwahrscheinlich der performanteste Ansatz sein. Es gibt verschiedene CLR-Level-Optimierungen, die sie unglaublich schnell arbeiten lassen. In diesem Fall würde ich jedoch nicht auf IEnumerable<T> - ich würde es einfach als Array ausgeben. Dadurch wird das Iterieren schneller, als wenn der Compiler GetEnumerator() aufrufen muss.

Wenn der C # -Compiler eine foreach -Anweisung für ein Array sieht, generiert er Aufrufe, um direkt zum Indexer zu gehen und die Length -Eigenschaft zu verwenden ... und dann kann die CLR ebenfalls die Überprüfung von Grenzen entfernen das Muster.

Ebenso, wenn Sie sich entscheiden, mit List<T> zu gehen, lassen Sie es als List<T> - so erhalten Sie List<T>.Enumerator - was eine Struktur ist - direkt, ohne Boxen.

BEARBEITEN: Steve Megson bringt den Punkt auf, LINQ dafür zu benutzen. Tatsächlich können Sie wahrscheinlich besser als das tun, denn sobald Sie den Enumerator der zugrunde liegenden Liste haben, können Sie das sicher zurückgeben, zumindest für alle Sammlungen, die ich kenne . So könnte man haben:

%Vor%

Das bedeutet, dass beim Iterieren nur ein winziger Treffer auftritt - nur der einzelne delegierte Aufruf an GetEnumerator() . Vergleichen Sie das mit Enumerable.Select , das bei jeden Aufruf auf MoveNext() (sowie die No-Op-Projektion) einen zusätzlichen Delegationstreffer benötigt.

    
Jon Skeet 03.11.2010, 20:42
quelle
5

Wenn es konstant ist, würde ich mit dem Array gehen, da es keine zusätzliche Funktionalität für das Hinzufügen und Entfernen von Elementen in einer Liste gibt. Array wird auch einen minimalen Speicherbedarf haben.

    
Jackson Pope 03.11.2010 20:41
quelle
4

Während Jon die Frage beantwortet hat, die Liste unveränderlich zu machen, möchte ich auch darauf hinweisen, dass selbst wenn die Liste unveränderlich ist, ihre enthaltenen Objekte sind nicht automatisch unveränderlich.

Selbst wenn Sie die Liste unveränderlich machen (zB indem Sie ihren Inhalt in ReadOnlyCollection<T> kopieren), können Sie weiterhin Eigenschaften der enthaltenen Objekte manipulieren, wenn sie nicht von unveränderlichem Typ sind . Sobald Sie Verweise auf die in der Liste enthaltenen Objekte ausgeben, kann der Aufrufcode diese Objekte manipulieren.

Nehmen wir ein Beispiel:

%Vor%

Dann haben wir den folgenden Code:

%Vor%

Wie Sie sehen können, ist * Person , obwohl wir die -Liste effektiv unveränderlich gemacht haben, kein unveränderlicher Typ. Da die Persons -Eigenschaft der Group -Klasse Verweise auf die tatsächlichen Person -Objekte ausgibt, kann der aufrufende Code das Objekt leicht manipulieren.

Eine Möglichkeit, sich davor zu schützen, besteht darin, die Sammlung als IEnumerable<T> unter Verwendung von yield return und einem bestimmten Klonierungsmechanismus verfügbar zu machen, um sicherzustellen, dass Sie die ursprünglichen Objektreferenzen nicht ausgeben. Zum Beispiel können Sie die Person -Klasse in diese ändern:

%Vor%

Nun wird das obige Programm nicht den Namen der Person Instanz in der Liste ändern, sondern seine eigene Kopie. Beachten Sie jedoch, dass wir jetzt etwas haben, was man als shallow immutability bezeichnen kann: Wenn Person wiederum Member haben würde, die nicht unveränderbar sind, existiert dasselbe Problem für diese Objekte und so weiter ...

Eric Lippert hat 2007 eine 10-teilige Serie von Blogposts zum Thema geschrieben. Der erste Teil ist hier: Unverwechselbarkeit in C # Teil eins: Arten der Unveränderlichkeit .

    
Fredrik Mörk 03.11.2010 22:34
quelle
2

Sie könnten sich ReadOnlyCollection<T> ansehen, die eine bestehende IList<T & gt; als schreibgeschützt und kann in IEnumerable<T>

umgewandelt werden

Ссылка

    
Joshua Rodgers 03.11.2010 20:42
quelle
0

AFAIK, wegen der weniger komplexen Struktur, wäre die Regel: Wenn Sie die Größe der Sammlung erhöhen oder verringern müssen, verwenden Sie die Liste, wenn nicht, und Sie verwenden es nur für die Enumeration, verwenden Sie Array.

>

Ich glaube nicht, dass die Konstante gegenüber der regulären Variablen bei der Instanziierung des Objekts viel Unterschied macht.

    
Hal 03.11.2010 20:53
quelle

Tags und Links