Für mich bedeutet IEnumerable<T>
in C # (na ja, .NET) eine beliebige Menge von Daten, die iteriert werden können. Es kann durch alles unterstützt werden, wie die Ergebnisse einer SELECT
-Abfrage, der Inhalt eines Arrays, Zeichen, die vom Benutzer in der Konsole eingegeben wurden, oder die Ziffern von Pi. Die Daten können nicht nach Index referenziert werden, sie ist nicht notwendigerweise endlich oder unendlich und kann auch nicht modifiziert werden. Es könnte sogar eine andere Datenmenge sein, wenn sie später identisch aufgerufen wird, wie zB IEnumerable<double>
von Zufallszahlen. Es sind nur Daten, die einem Verbraucher wie eine foreach
-Schleife zurückgegeben werden.
Betrachten Sie nun ein Konzept in etwas anderem, das sich mit Datensätzen befasst: SQL. In SQL wird die Reihenfolge der Zeilen, sofern nicht explizit angegeben, nicht garantiert und ist nicht relevant. Wenn Sie beispielsweise SELECT * FROM stack_overflow_posts LIMIT 1
ausführen, wird von der Datenbank nicht impliziert, dass die zurückgegebene Zeile tatsächlich die erste Zeile ist, die eingefügt wurde, noch die älteste Zeile. Sie müssen die Ergebnisse beispielsweise explizit mit ORDER BY posted_date_time
anordnen.
Gilt das gleiche Konzept für Aufzählungen in .NET mit IEnumerable<T>
? Ist die Verwendung von IEnumerable<T>
eine Implikation, dass Ergebnisse immer in einer bestimmten Reihenfolge geliefert werden? In den Beispielen, die ich früher gegeben habe, würde ich ja sagen, eine Reihenfolge wäre implizit, denn wenn sie in aufgelistet wären eine andere Reihenfolge, die Ergebnisse wären bedeutungslos; Wenn Sie die vom Benutzer in der Konsole eingegebenen Zeichen in einer anderen Reihenfolge als die tatsächlichen Tastenanschläge erhalten, warum sollten Sie sie lesen? Offensichtlich hat LINQ OrderBy()
, um Ergebnisse zu sortieren, wie Sie wollen, aber das ist eine explizite Reihenfolge, nicht implizit.
Eine Klasse, die eine Schnittstelle implementiert, verspricht effektiv, einem bestimmten Muster zu folgen und nicht nur Methoden zu implementieren, wie sie von der Schnittstelle definiert werden. Bedeutet IEnumerable<T>
, dass seine Daten in einer relevanten Reihenfolge geliefert werden, oder ob es den Verbrauchern der Aufzählung obliegt, explizit zu bestellen, wenn sie dies wünschen? Wenn ich eine Methode habe, die Artikel in einer undefinierten Reihenfolge liefert - oder besser gesagt, eine Bestellung, die für die Konsumenten nicht relevant ist und jederzeit geändert werden kann - sollte ich etwas anderes als IEnumerable<T>
verwenden?
Bedeutet die Verwendung von
IEnumerable<T>
, dass Ergebnisse immer in einer bestimmten Reihenfolge geliefert werden?
Nein. IEnumerable
garantiert nur, dass das Objekt iteriert werden kann. Die Tatsache, dass beispielsweise List<T>
immer Elemente in aufsteigender Reihenfolge nach Index ausgibt, ist ein Implementierungsdetail für List<T>
spezifisch.
Eine Klasse, die eine Schnittstelle implementiert, verspricht effektiv, einem bestimmten Muster zu folgen und nicht nur Methoden zu implementieren, wie sie von der Schnittstelle definiert werden. Bedeutet
IEnumerable<T>
, dass seine Daten in einer relevanten Reihenfolge ausgegeben werden, oder liegt es an den Verbrauchern der Aufzählung, explizit zu bestellen, wenn sie dies wünschen?
Nein, die Implementierung von IEnumerable
impliziert keine Reihenfolge. Wenn Sie ein Objekt konsumieren, das IEnumerable
ist, müssen Sie explizit eine Reihenfolge angeben, wenn Sie sicherstellen möchten, dass Ihre Daten jedes Mal in der gleichen Reihenfolge ausgegeben werden.
Wenn Sie an die CLR-Auflistungstypen denken, die IEnumerable
implementieren, ist dies einfach. Stellen Sie sich vor, Sie haben eine Methode erstellt, die IEnumerable<string>
zurückgegeben hat. Diese Methode könnte eine List<string>
zurückgeben, deren Implementierung von IEnumerable
eine bestimmte Reihenfolge hat, aber sie könnte ebenso einfach eine HashSet<string>
zurückgeben, für die eine Anweisung nicht gilt Sinn.
Wenn ich eine Methode habe, die Artikel in einer undefinierten Reihenfolge liefert - oder besser gesagt, eine Bestellung, die für den Verbraucher nicht relevant ist und jederzeit geändert werden kann - sollte ich etwas anderes als
IEnumerable<T>
verwenden?
Ich würde sagen, dass IEnumerable<T>
gut zu Ihren Bedürfnissen passt. Um besonders klar zu sein, könnten Sie Ihre Methode dokumentieren und angeben, dass die Reihenfolge der Elemente im Ergebnis nicht definiert ist und sich von Aufruf zu Aufruf ändern könnte.
IEnumerable<T>
garantiert keine Bestellung, aber der Implementierer oder die Methode, die IEnumerable<T>
zurückgibt, könnte die Bestellung garantieren. Zum Beispiel liefert File.ReadLines
garantiert Dateizeilen in der richtigen Reihenfolge. Enumerable.Where
wird garantiert beibehalten Reihenfolge. Wie Sie bereits erwähnt haben, können Sie jedoch leicht eine Methode schreiben, die jedes Mal eine neue GUID liefert und keine Reihenfolge hat.
Eine unglückliche Einschränkung von IEnumerable<T>
ist, während es zahlreiche Merkmale gibt, die die meisten Implementierungen haben, und einige Verbraucher verlassen sich darauf, dass es keine Mittel gibt, mit denen Objekte angeben können, ob sie solche Eigenschaften haben.
Zu diesen Merkmalen gehört die Tatsache, dass, wenn eine Methode, die ein IEnumerable<T>
aufruft, GetEnumerator
aufruft und den Inhalt der Sammlung durchläuft, nach einer endlichen Anzahl von MoveNext
-Aufrufen das Ende erreicht; Wenn es GetEnumerator
erneut aufruft, ohne etwas anderes mit dem übergebenen IEnumerable<T>
ausgeführt zu haben, sollte es die gleiche Sequenz von Elementen in derselben Reihenfolge erhalten und am selben Punkt enden.
Beachten Sie, dass das Obige tatsächlich eine Anzahl von diskreten Kriterien umfasst; Es gibt viele Implementierungen, die alle erfüllen, aber es ist möglich, dass Implementierungen fast jede Kombination erfüllen. Unter den verschiedenen Kriterien aus der obigen Aussage:
Dass eine Aufzählung eine endliche Anzahl von Elementen ergibt.
Bei mehreren Aufzählungen ergibt sich immer die gleiche Anzahl von Elementen.
Alle Elemente, die in einer Iteration vorkommen, erscheinen in derselben Reihenfolge in einer anderen.
Wenn eine Aufzählung keine endliche Anzahl von Elementen ergibt, ist die Frage, ob mehrere Iterationen die gleiche Anzahl ergeben, strittig. Abgesehen davon ist es jedoch möglich, dass eine Implementierung von IEnumerable<T>
sich an eine der sechs verbleibenden Kombinationen der obigen Kriterien hält:
Viele Sammlungen werden, wenn sie nur von einem Thread verwendet werden, natürlich alle drei enthalten.
Ein "wahrer" Zufallsgenerator wird möglicherweise nicht von irgendjemandem gehalten.
Ein Pseudozufallsgenerator kann nur an # 3 hängen.
Eine gleichzeitige Add-Only-Liste könnte zwischen # 1 und # 3 stehen.
Einige andere gleichzeitige Sammlungen halten sich möglicherweise nur an # 1.
Ein Array, das von gleichzeitigem Code verwendet wird, kann nur zwischen # 1 und # 2 bestehen.
Obwohl einige Methoden, die ein IEnumerable<T>
erhalten, fehlschlagen können, wenn das übergebene Objekt einige oder alle der oben genannten Merkmale nicht erfüllt, sind die Merkmale nicht Teil des IEnumerable<T>
Vertrags, noch gibt es einen Weg für einen IEnumerable<T>
um anzugeben, welche Kriterien erfüllt werden können. Die Erwartung scheint zu sein, dass Code, der eine IEnumerable<T>
an eine Methode weitergibt, dafür verantwortlich ist, zu wissen, welche "Art" von IEnumerable<T>
er hat und welche Methoden eine von externen Code empfangene IEnumerable<T>
an eine Methode übergeben sollte irgendwie den Konsumenten die Anforderungen der Methoden vermitteln, an die die IEnumerable<T>
Instanzen übergeben werden.
Wenn ein IEnumerable<T>
einmal aufgelistet wird, werden Elemente in einer bestimmten Reihenfolge zurückgegeben. Bei den meisten IEnumerable<T>
-Implementierungen ergeben wiederholte Enumerationen die Elemente in der gleichen Reihenfolge, und einige Codes basieren auf diesem Verhalten. Im IEnumerable<T>
-contract gibt es jedoch nichts, was dies angibt. .
Tags und Links .net c# linq ienumerable