Geordnete Aufzählung: IEnumerable oder Array (in C #)?

8

Typischer Kontext: Ich erstelle eine Erweiterungsmethode für eine Sammlung, die berücksichtigt, dass die Elemente geordnet sind. Die Funktion beginnt am Anfang, am Index 0, und die Reihenfolge hat Bedeutung. Beispiele: Gruppierung nach Reihenfolge oder Indizes eines Elements .

Ich bin jedoch immer ratlos, was ich erweitern soll: IEnumerable<T>oder T[]. Meine Begründung war die Klarheit des Zwecks: Ein Array hat einen Begriff der Reihenfolge, während ein IEnumerable von vielen generischen Sammlungen implementiert wird, von denen nicht alle einen Begriff der Reihenfolge haben:

  • Wörterbuch - ungeordnet
  • HashSet - ungeordnet
  • LinkedList - bestellt
  • Liste - bestellt
  • Warteschlange - bestellt
  • SortedDictionary - sortiert (nicht ursprüngliche Reihenfolge)
  • SortedList - sortiert (nicht Originalbestellung)
  • SortedSet - sortiert (nicht Originalbestellung)
  • Stapel - umgekehrt

Sowie jede andere Implementierung, die bestellt werden kann oder nicht.

Ich bin mir auch nicht sicher, ob der Enumerator zurückgesetzt wird, wenn eine Enumeration nicht abgeschlossen ist. Wenn dies also eine Sorge ist, wer weiß dann, an welchem ​​Punkt die Enumeration beginnen würde? Das Aufzählen eines Arrays würde immer am Anfang beginnen.

Für mich ist es also sinnvoller, a zu erweitern T[]. Aber bin ich richtig, wenn ich das denke? Mache ich mir zu viele Sorgen? Was ist der richtige Ansatz, um eine "geordnete" Aufzählung sicherzustellen?

MPelletier
quelle

Antworten:

13

Ich bin nicht sicher, ob der Enumerator zurückgesetzt wird, wenn eine Enumeration nicht abgeschlossen ist. Wenn dies also eine Sorge ist, wer weiß dann, an welchem ​​Punkt die Enumeration beginnen würde? Das Aufzählen eines Arrays würde immer am Anfang beginnen.

Diese beiden Sätze lassen mich denken, dass Sie tiefe Missverständnisse darüber haben, wie das aufzählbare Muster funktioniert. Können Sie erklären, warum Sie glauben, dass ein verlassener Enumerator irgendetwas mit einer späteren Aufzählung zu tun hat ?

Ein Enumerable ist ein Gerät, das Enumeratoren erzeugt . Wenn ein Enumerator aufgegeben wird, hat dies keinerlei Auswirkungen auf die Aufzählung. Wenn ich Bücher verkaufe und Bob ein Buch kauft und es nur zur Hälfte liest, heißt das nicht, dass sie, wenn ich Alice ein anderes Exemplar des Buches verkaufe, dort anfangen muss, wo Bob aufgehört hat.

Ich bin immer ratlos, was ich erweitern soll: IEnumerable<T>oder T[]. Meine Begründung war die Klarheit des Zwecks: Ein Array hat einen Begriff der Reihenfolge, während ein IEnumerable von vielen generischen Sammlungen implementiert wird, von denen nicht alle einen Begriff der Reihenfolge haben:

Muss Ihre Erweiterungsmethode (1) nicht in der richtigen Reihenfolge auf die Sammlung zugreifen? (2) in die Sammlung schreiben?

Wenn ja, dann verlängern IList<T>Wenn nicht, verlängern IEnumerable<T>.

Was ist mit: (3) Weitergabe der Sammlung an eine Methode, die ein Array erwartet?

Erweitern Sie dann das Array.

Ihre Frage lautet im Grunde "Ich weiß nicht, ob ich Animal oder Giraffe erweitern soll". Wenn Ihre Erweiterungsmethode nicht spezifisch für Giraffen ist, erweitern Sie Animal. Wenn Ihre Erweiterungsmethode nicht spezifisch für Arrays ist, erweitern Sie alle Listen. Wenn es nicht spezifisch für Listen ist, erweitern Sie alle Sequenzen. Wenn es nicht sequenzspezifisch ist, stehen die Chancen gut, dass eine Erweiterungsmethode der falsche Mechanismus für Sie ist. Ich empfehle, nicht alle Objekte zu erweitern.

Eric Lippert
quelle
Ich habe einen großen, beschämenden Mangel an Wissen darüber, wie das aufzählbare Muster funktioniert. Ich denke, deshalb frage ich hier.
MPelletier
Verdammt, ich hatte meine Datenstrukturklasse völlig vergessen. Natürlich hat dies keine Auswirkungen auf spätere Aufzählungen! Ich habe mich beschämt ... :(
MPelletier
@ MPelletier: Ihre Frage hat immer noch Wert. Ist es sicher / ist es sinnvoll, einen Algorithmus auszuführen, bei dem die Reihenfolge z. B. a von Bedeutung ist Dictionary? Wahrscheinlich nicht. Wie Scott Meyers sagte: "Der beste Weg, um eine falsche Verwendung zu verhindern, besteht darin, eine solche Verwendung unmöglich zu machen." . Ist dies in diesem Szenario möglich / sinnvoll?
Steven Jeuris
1
@EricLippert: Die 3 Punkte , die Sie erwähnt sind Gründe , warum Sie haben Array zu erweitern. Die eigentliche Frage ist, wie in meinem vorherigen Kommentar, ob es sich lohnt, eine falsche Verwendung zu verhindern.
Ich hoffe,
@StevenJeuris Ihre Frage wäre nicht erforderlich, wenn C # ordnungsgemäß entworfen worden wäre und über eine ISortedCollection <T> -Schnittstelle verfügt, die Sie erweitern würden, wenn die Sammlung sortiert werden müsste. Da dies jedoch nicht der Fall ist, besteht die einzige Möglichkeit, Missbrauch beim Kompilieren zu verhindern, darin, entweder einen zu engen Typ (IList <T>) oder IEnumerable <T> zu erweitern und eine unnötige Sortierung durchzuführen.
Jim Balter
2

Ich denke, dass Sie 2 verschiedene Konzepte zu 1 kombinieren möchten. Die Datenstruktur und ihre Inhaltsreihenfolge sind zwei getrennte Dinge.

Bestellen ist ein heikles Thema. Was bedeutet es zum Beispiel, eine Personenliste zu bestellen? Selbst wenn der Bestell- / Sortierschlüssel definiert ist, muss er eine Richtung haben und Sie müssen entscheiden, was mit Nullen (falls vorhanden), Datumsproblemen usw. geschehen soll.

Wenn die Datenreihenfolge für Ihre Methode wichtig ist, sollte Ihre Methode möglicherweise für die Reihenfolge der Daten im Rahmen ihrer Einrichtung verantwortlich sein, anstatt zu verlangen, dass die Daten vom Anrufer bestellt werden. Ein ähnlicher Ansatz wird von einigen String-Match-Algorithmen verwendet, bei denen ein Wörterbuch innerhalb der Suchmethode aus der übergebenen Zeichenfolge erstellt wird, bevor die Zeichenfolge durchsucht wird. Ich weiß, dass dies nicht der genaue Fall ist, aber es ist das nächste Beispiel, an das ich denken könnte.

Keine Chance
quelle
2
Eine Bestellmethode als Argument ... Hmmm, interessant!
MPelletier
Selbst dann könnte die Reihenfolge implizit sein. Das ist das Hauptproblem, das ich habe. Ich verstehe, dass das Konzept von LINQ darin besteht, Datenbankfunktionen zu emulieren, und gemäß 1NF sind Datensätze ungeordnet. Leider ist die reale Welt selten 1NF.
MPelletier
"Die reale Welt ist selten 1NF", stimmt in einigen Fällen, aber ich bin mir nicht sicher, wie sich das auf den hier beschriebenen Fall auswirken würde.
NoChance
1NF : "Es gibt keine Reihenfolge von oben nach unten für die Zeilen." Das ist es, was hier passiert, denke ich. Es gibt eine Reihenfolge von oben nach unten in Protokollen, in vielen losen "Sammlungen". LINQ wurde entwickelt, um Sammlungen so zu bearbeiten, als wären sie Datenbanken, aber es kann eine Reihenfolge von oben nach unten geben, daher der Widerspruch, den ich gerade gesehen habe.
MPelletier
1

IEnumerable erklärt, dass die Sammlung, über die Sie verfügen, aufgelistet werden kann. es impliziert nichts über den Inhalt. Dies ist keine schlechte Sache - Container können zusätzlich zu einzelnen Schnittstellen zusätzliche Funktionen und Einschränkungen aufweisen.

(Ich sehe nicht, wie ein Array hier besser ist - es ist indizierbar, ebenso wie eine IList und andere Container, aber es gibt nichts an der Indizierung, was eine Datenreihenfolge impliziert. Ihre Liste ist seltsam - einige Elemente sind "geordnet" "Da sie die Einfügereihenfolge beibehalten, werden andere" sortiert ", wie sie sortiert sind. Es handelt sich um zwei verschiedene Dinge. Abgesehen vom SortedSet / SortedList / SortedDictionary sollten Sie nicht davon ausgehen, dass sich die Daten in einer bestimmten Reihenfolge befinden, es sei denn, Sie behandeln dies in Ihrem eigenen Code.)

IEnumerable bietet jedoch eine LINQ-Erweiterung, mit der Sie .OrderBy verwenden können, sodass alles, was aufzählbar ist, in einer sortierten Reihenfolge von IOrderedEnumerable zurückgegeben werden kann. Dies ist jedoch eine LINQ-spezifische Schnittstelle (und nicht standardmäßig von SortedSet / SortedList / SortedDictionary implementiert, daher ist es möglicherweise nicht sinnvoll, diese bestimmte Schnittstelle zu erweitern ...)

Joe
quelle
2
Was ich vermeiden möchte, ist eine Reihe von Funktionen zu erstellen, die "nur bei Bestellung" funktionieren. Monate später verwendet jemand die Bibliothek für eine ungeordnete Sammlung und fragt sich, warum sie funky Ergebnisse liefert. Neben einer guten Dokumentation, die natürlich jeder gründlich gelesen hat.
MPelletier
Und ich denke nicht, dass die Erweiterung von IOrderedEnumerable mich zu Fans machen wird. Einige Listen sind bereits bestellt (denken Sie beispielsweise an eine analysierte Protokolldatei. Die Reihenfolge ist die Zeilennummer, aber es macht keinen Sinn, danach zu sortieren , wenn sie nacheinander gelesen wurde.
MPelletier
Es gibt heute keinen Dekorateur / keine Benutzeroberfläche, die / der Benutzer darüber informieren / warnen kann, dass für Ihre Erweiterungsmethode eine sortierte Sammlung erforderlich ist. Wie würden Sie insbesondere bei beliebigen Daten (ohne Vergleichsfunktion) wissen, ob die Daten bestellt wurden oder nicht?
Joe