2009-09-08 11 views
6

Die IList-Schnittstelle erfordert eine Add-Methode. Arrays implementieren diese Funktion, aber es löst einfach eine NotImplementedException aus. Das scheint mir sehr schlecht zu sein.Warum unterstützen Arrays IList?

Was dachten die Designer, als sie das taten?

Antwort

7

ILists können nur gelesen werden - im Zweifelsfall kann der Aufrufer die IsFixedSize-Eigenschaft testen, bevor er versucht, ein Element hinzuzufügen oder zu entfernen, oder die IsReadOnly-Eigenschaft, bevor er versucht, ein Element zu ändern.

Ein Array ist eine IList mit fester Größe.

Es kann praktisch sein, ein Array als Liste zu behandeln. Ein Beispiel ist das Mocking einer Datenzugriffsmethode, die eine IList zurückgibt - sie kann verspottet werden, um ein Array einfach als IList zurückzugeben.

+1

Es ist eigentlich die 'IsFixedSize' Eigenschaft, die Sie überprüfen müssen. 'IsReadOnly' wird für Arrays" falsch "sein, da die vorhandenen Elemente geändert werden können. 'IsFixedSize' wird 'wahr', weil Elemente nicht hinzugefügt oder entfernt werden können. – LukeH

+0

Nur um das Problem mit festen und nur lesen zu klären: http://blogs.msdn.com/ericlippert/archive/2009/08/27/what-s-the-difference-between-fixed-and-fixed.aspx – Oliver

+1

@Oliver: Ich bin ein großer Fan von Eric Lipperts Blog, aber dieser Artikel hat absolut nichts mit den Eigenschaften "IsFixedSize" oder "IsReadOnly" zu tun. – LukeH

0

Da es für verschiedene Objekte üblich ist, unterschiedliche Fähigkeiten zu haben, enthalten einige Schnittstellen Elemente, die von einigen, aber nicht allen Implementierungen implementiert werden. Wenn einige, aber nicht alle Implementierungen einer hypothetischen Schnittstelle IVehicle einen Anhänger anhängen könnten, wäre ein typisches Muster die Definition der Funktion AttachTrailer als "Versuch, einen Anhänger anzuhängen, wenn CanAttachTrailer wahr ist, oder andernfalls NotSupportedException werfen". . Alle Implementierungen von IVehicle könnten der obigen Spezifikation entsprechen, unabhängig davon, ob sie Anhänger handhaben können oder nicht.

Ein Vorteil dieses Ansatzes besteht darin, dass es für Implementierungen einer Schnittstelle möglich ist, viele verschiedene Kombinationen von Merkmalen anzubieten, ohne unterschiedliche Typen für verschiedene Kombinationen definieren zu müssen. Ein weiterer Vorteil dieses Ansatzes besteht darin, dass eine Methode Foo, die ein Objekt mit Funktionen, die Foo nicht benötigt, empfängt, an die Methode Bar weitergeleitet wird, die diese Funktionen benötigt, ohne dass irgendwo eine Typumwandlung erforderlich ist ohne Foo zu wissen, welche Fähigkeiten Bar benötigen wird. Ein weiterer Vorteil besteht darin, dass es so einfach ist, Code zu schreiben, der bestimmte Fähigkeiten nicht benötigt, aber sie nutzen kann, wenn sie vorhanden sind.

Dieser Ansatz hat jedoch einige Nachteile. Es gibt noch keine Möglichkeit für eine Schnittstelle, Standardimplementierungen für beliebige Eigenschaften oder Methoden anzugeben. Folglich müssen selbst Implementierungen, die Anhänger nicht anhängen können, Code enthalten, um false an die CanAttachTrailer Eigenschaft zurückzugeben und eine Ausnahme in ihrer AttachTrailer Methode zu werfen. Da die Schnittstelle keine Anforderungen stellt, dass viele ihrer Methoden implementiert werden, gibt es keine Möglichkeit, dass ein Compiler beim Versuch, eine Funktion aufzurufen, die eine Fähigkeit mit einem Objekt eines Typs benötigt, das diese nicht bereitstellen kann, zurückweisen kann.

Bei der Entwicklung von IList<T> dachte Microsoft offenbar, dass die Vorteile des Ansatzes "optionale Schnittstellen" die Nachteile überwogen. Wenn .net ein Mittel für Klassen bereitstellt, die Interfaces implementieren, um auf Standardimplementierungen von Membern zu verzichten, die sie nicht bereitstellen möchten, gäbe es wenig Grund, viele optionale Fähigkeiten nicht in Basisebenen-Interfaces aufzunehmen; Um die Kompilierungszeit der erforderlichen Fähigkeiten zu ermöglichen, könnte man eine Anzahl von Schnittstellen von einer Basis ableiten, die alle benötigten Mitglieder enthält, und spezifizieren, dass Klassen, die die letzteren Schnittstellen implementieren, bestimmte Mitglieder auf eine Weise implementieren müssen, die tatsächlich nützlich ist. Zum Beispiel könnte Microsoft IResizableList<T> definiert haben, um IList<T> zu erben, ohne irgendwelche Mitglieder hinzuzufügen, aber mit der Erwartung, dass IList<T> Implementierungen, die die Größenänderung erlaubten, die letztere Schnittstelle implementieren würden, während diejenigen, die die Größenänderung nicht erlaubten, diese nicht implementieren würden.Hätten sie das getan, könnte Code, der in der Lage sein musste, die Größe einer Liste zu ändern, IResizableList<T> verlangen (in diesem Fall würde er kein Array akzeptieren), während Code, der keine Größe ändern musste, IList<T> verlangen konnte). Leider hat Microsoft so etwas nicht gemacht. Daher ist es für Code nicht möglich, eine kompilierbare Größe in der Größe zu verlangen - er kann nur quatschen, wenn sich eine übergebene Liste als feste Größe meldet.