Warum implementiert Array IList?

140

Siehe die Definition der System.Array- Klasse

public abstract class Array : IList, ...

Theoretisch sollte ich in der Lage sein, dieses Stück zu schreiben und glücklich zu sein

int[] list = new int[] {};
IList iList = (IList)list;

Ich sollte auch in der Lage sein, jede Methode aus der iList aufzurufen

 ilist.Add(1); //exception here

Meine Frage ist nicht, warum ich eine Ausnahme bekomme, sondern warum Array IList implementiert .

oleksii
quelle
21
Gute Frage. Ich mochte die Idee von fetten Schnittstellen nie (das ist der Fachbegriff für diese Art von Design).
Konrad Rudolph
2
Interessiert sich jemand wirklich für LSP? Es scheint mir ziemlich akademisch.
Gabe
13
@Gabe, dann musst du mit größeren Codebasen arbeiten. Das Implementieren eines Verhaltens (Erben von einer Schnittstelle) und das einfache Ignorieren der Dinge, die Sie nicht mögen / nicht unterstützen können, führt zu stinkendem, verschleiertem, Casting und schließlich zu fehlerhaftem Code.
Marius
3
@Gabe ist die Sammlung, die Veränderlichkeit impliziert, nicht die enthaltenen Entitäten. Sie können Ihre Klasse zu einem Typ machen, der sowohl IRWList <> als auch IReadList <> implementiert. Verwenden Sie if als IRWList <> intern in Ihrer Klasse und machen Sie es als IReadList verfügbar. Ja, Sie müssen die Komplexität irgendwo hinstellen, aber ich sehe nur nicht ein, wie dies für die Missachtung von LSP als sehr gutes Designprinzip gilt (wusste jedoch nichts über die IsReadOnly-Eigenschaft, die IList aus Verbrauchersicht komplexer macht)
Marius

Antworten:

93

Da ein Array einen schnellen Zugriff über den Index ermöglicht und IList/ IList<T>is die einzigen Sammlungsschnittstellen sind, die dies unterstützen. Ihre eigentliche Frage lautet also vielleicht: "Warum gibt es keine Schnittstelle für konstante Sammlungen mit Indexern?" Und darauf habe ich keine Antwort.

Es gibt auch keine schreibgeschützten Schnittstellen für Sammlungen. Und ich vermisse diese noch mehr als eine konstante Größe mit Indexer-Oberfläche.

IMO sollte es abhängig von den Merkmalen einer Sammlung mehrere (generische) Sammlungsschnittstellen geben. Und die Namen hätten auch anders sein sollen, Listdenn etwas mit einem Indexer ist IMO wirklich dumm.

  • Nur Aufzählung IEnumerable<T>
  • Schreibgeschützt, aber kein Indexer (.Count, .Contains, ...)
  • Größenänderbar, aber kein Indexer, dh wie (Hinzufügen, Entfernen, ...) aktuell eingestellt ICollection<T>
  • Schreibgeschützt mit Indexer (Indexer, Indexof, ...)
  • Konstante Größe mit Indexer (Indexer mit Setter)
  • Variable Größe mit aktuellem Indexer (Insert, ...) IList<T>

Ich denke, die aktuellen Kollektionsoberflächen sind schlecht gestaltet. Da sie jedoch Eigenschaften haben, die Ihnen sagen, welche Methoden gültig sind (und dies ist Teil des Vertrags dieser Methoden), verstößt dies nicht gegen das Substitutionsprinzip.

CodesInChaos
quelle
14
Danke für die Antwort. Aber ich lasse die Frage lieber so wie sie ist. Der Grund ist einfach. Schnittstelle ist ein öffentlicher Auftrag. Wenn man es implementiert, muss man alle Mitglieder vollständig implementieren, sonst bricht es LSP und riecht im Allgemeinen schlecht, nicht wahr?
Oleksii
16
Es bricht LSP. Wenn es nicht aufgelistet wurde. Hinzufügen (Element) sollte Element zur Liste hinzufügen, unabhängig vom konkreten Typ. Mit Ausnahme von Ausnahmefällen. In der Array-Implementierung wird eine Ausnahme in einem Nicht-Ausnahmefall ausgelöst, was an sich eine schlechte Praxis ist
Rune FS
2
@smelch Es tut mir leid, aber du hast dann LSP falsch verstanden. Ein Array wird nicht implementiert addund kann daher nicht durch etwas ersetzt werden, das dies tut, wenn diese Fähigkeit erforderlich ist.
Rune FS
7
Ich gebe zu, dass es technisch nicht gegen LSP verstößt, nur weil die Dokumentation besagt, dass Sie die IsFixedSizeund IsReadOnlyEigenschaften überprüfen sollten. Es verstößt definitiv gegen das Tell, Don't Ask-Prinzip und das Prinzip der geringsten Überraschung . Warum eine Schnittstelle implementieren, wenn Sie nur Ausnahmen für 4 der 9 Methoden auslösen?
Matthew
11
Seit der ursprünglichen Frage ist einige Zeit vergangen. Aber jetzt mit .Net 4.5 gibt es zusätzliche Schnittstellen IReadOnlyList und IReadOnlyCollection .
Tobias
42

Der Bemerkungsabschnitt der Dokumentation für IListsagt

IList ist ein Nachkomme der ICollection-Schnittstelle und die Basisschnittstelle aller nicht generischen Listen. IList-Implementierungen lassen sich in drei Kategorien einteilen: schreibgeschützt, feste Größe und variable Größe . Eine schreibgeschützte IList kann nicht geändert werden. Eine IList mit fester Größe ermöglicht nicht das Hinzufügen oder Entfernen von Elementen, sondern das Ändern vorhandener Elemente. Eine IList mit variabler Größe ermöglicht das Hinzufügen, Entfernen und Ändern von Elementen.

Offensichtlich fallen Arrays in die Kategorie mit fester Größe, so dass es durch die Definition der Schnittstelle sinnvoll ist.

Brian Rasmussen
quelle
4
Ich denke, sie hätten am Ende viele Schnittstellen gehabt. IListFixedSize, IListReadOnly ...
Magnus
9
Das ist aus Sicht der Dokumentation eine gute Antwort. Aber für mich sieht es eher nach einem Hack aus. Schnittstellen müssen dünn und einfach sein, damit eine Klasse alle Mitglieder implementieren kann.
Oleksii
1
@oleksii: Ich stimme zu. Schnittstellen und Laufzeitausnahmen sind nicht die eleganteste Kombination. Zur Verteidigung Arraywird die AddMethode explizit implementiert , wodurch das Risiko eines versehentlichen Aufrufs verringert wird.
Brian Rasmussen
Bis wir eine Implementierung erstellen IList, die sowohl das Ändern als auch das Hinzufügen / Entfernen nicht zulässt . Dann sind die Dokumentationen nicht mehr korrekt. : P
Timo
1
@Magnus - In .Net 4.5 gibt es zusätzliche Schnittstellen IReadOnlyList und IReadOnlyCollection .
RBT
17

Weil nicht alle ILists veränderbar sind (siehe IList.IsFixedSizeund IList.IsReadOnly) und Arrays sich sicherlich wie Listen mit fester Größe verhalten.

Wenn Ihre Frage wirklich lautet: "Warum wird eine nicht generische Schnittstelle implementiert ?", Lautet die Antwort, dass diese vor der Einführung von Generika vorhanden waren.

user541686
quelle
10
@oleksii: Nein, es bricht LSP nicht, weil die Schnittstelle IList selbst Ihnen sagt, dass es möglicherweise nicht veränderbar ist. Wenn es in der Tat ist garantiert wandelbar zu sein , und das Array sagten Sie sonst, dann wäre es die Regel zu brechen.
user541686
Tatsächlich bricht Array LSP im Falle von Generika IList<T>und nicht im Falle von Nicht-Generika IList: Enterprisecraftsmanship.com/2014/11/22/…
Vladimir
5

Es ist ein Erbe, das wir aus Zeiten haben, in denen nicht klar war, wie man mit schreibgeschützten Sammlungen umgeht und ob Array schreibgeschützt ist oder nicht. In der IList-Schnittstelle befinden sich IsFixedSize- und IsReadOnly-Flags. Das IsReadOnly-Flag bedeutet, dass die Sammlung überhaupt nicht geändert werden kann, und IsFixedSize bedeutet, dass die Sammlung Änderungen zulässt, jedoch keine Elemente hinzufügt oder entfernt.

Zum Zeitpunkt von .Net 4.5 war klar, dass einige "Zwischen" -Schnittstellen erforderlich sind, um mit schreibgeschützten Sammlungen zu arbeiten, IReadOnlyCollection<T>und IReadOnlyList<T>wurden eingeführt.

Hier ist ein großartiger Blog-Beitrag, der die Details beschreibt: Nur-Lese-Sammlungen in .NET

Vladimir
quelle
0

Die Definition der IList-Schnittstelle lautet "Stellt eine nicht generische Sammlung von Objekten dar, auf die über den Index einzeln zugegriffen werden kann." Das Array erfüllt diese Definition vollständig und muss daher die Schnittstelle implementieren. Eine Ausnahme beim Aufrufen der Add () -Methode ist "System.NotSupportedException: Collection hatte eine feste Größe" und ist aufgetreten, weil das Array seine Kapazität nicht dynamisch erhöhen kann. Seine Kapazität wird beim Erstellen des Array-Objekts definiert.

meir
quelle
0

Wenn ein Array IList (und transitiv ICollection) implementiert, wird die Linq2Objects-Engine vereinfacht, da das Umwandeln der IEnumerable in IList / ICollection auch für Arrays funktionieren würde.

Beispielsweise ruft Count () die Array.Length unter der Haube auf, da sie in ICollection umgewandelt wird und die Implementierung des Arrays Length zurückgibt.

Ohne dies hätte die Linq2Objects-Engine keine spezielle Behandlung für Arrays und eine schreckliche Leistung, oder sie müsste den Code verdoppeln und eine Sonderbehandlung für Arrays hinzufügen (wie dies für IList der Fall ist). Sie müssen sich dafür entschieden haben, dass Array stattdessen IList implementiert.

Das ist meine Einstellung zu "Warum".

Herman Schönfeld
quelle
0

Außerdem Implementierungsdetails LINQ Letzte Überprüfungen für IList. Wenn die Liste nicht implementiert wurde, müssten entweder 2 Überprüfungen alle letzten Aufrufe verlangsamen oder Last auf einem Array mit O (N).

user1496062
quelle