Ich verstehe, dass, wenn S
eine Kindklasse von T
ist, ein List<S>
nicht ein Kind von List<T>
ist. Fein. Aber Interfaces haben ein anderes Paradigma: Wenn Foo
implementiert IFoo
, warum ist dann ein List<Foo>
nicht (ein Beispiel für) ein List<IFoo>
?
Da es keine tatsächliche Klasse IFoo
geben kann, bedeutet das, dass ich jedes Element der Liste immer darstellen müsste, wenn ein List<IFoo>
angezeigt wird? Oder ist das einfach ein schlechtes Design und ich muss meine eigene Collection-Klasse ListOfIFoos
definieren, um mit ihnen arbeiten zu können? Keine scheint mir vernünftig ...
Was wäre der beste Weg, eine solche Liste aufzudecken, wenn ich versuche, Schnittstellen zu programmieren? Ich tendiere derzeit dazu, mein List<Foo>
intern als List<IFoo>
zu speichern.
Ihre List<Foo>
ist keine Unterklasse wenn List<IFoo>
, weil Sie kein MyOwnFoo
Objekt darin speichern können, was auch eine IFoo
Implementierung ist. ( Liskov-Substitutionsprinzip )
Die Idee, ein List<IFoo>
anstelle eines dedizierten List<Foo>
zu speichern, ist OK. Wenn Sie den Inhalt der Liste in den Implementierungstyp umwandeln müssen, bedeutet dies wahrscheinlich, dass Ihre Schnittstelle nicht geeignet ist.
Hier ist ein Beispiel, warum Sie es nicht tun können:
%Vor%An dieser Stelle enthält eine Liste, die zum Speichern von MemoryStreams erstellt wurde, jetzt ein Formular. Schlecht!
Im Grunde ist diese Einschränkung vorhanden, um die Typensicherheit zu erhalten. In C # 4 und .NET 4.0 wird es begrenzte Unterstützung dafür geben (es heißt variance ), aber es wird dieses spezielle Szenario aus genau den oben genannten Gründen immer noch nicht unterstützen .
In Ihrer Rückkehrfunktion müssen Sie die Liste zu einer Liste von Interfaces machen, und wenn Sie das Objekt erstellen, machen Sie es als ein Objekt, das es implementiert. So:
%Vor%
MASSIV BEARBEITEN
Sie können es mit C # 4.0 machen, aber [danke Jon]
Sie können es umgehen mit ConvertAll
:
Die einfache Antwort ist, dass List<Foo>
ein anderer Typ als List<IFoo>
ist, genauso wie DateTime
sich von IPAddress
unterscheidet, zum Beispiel.
Die Tatsache, dass Sie IFoo
haben, bedeutet jedoch, dass Sammlungen von IFoo
mindestens zwei Implementierungen von IFoo
( FooA
, FooB
, etc ...) enthalten sollen, weil Sie dies erwarten Da es immer nur eine Implementierung von IFoo
, Foo
gibt, ist der IFoo
-Typ redundant.
Wenn es also immer nur einen abgeleiteten Typ einer Schnittstelle geben wird, vergessen Sie die Schnittstelle und sparen Sie den Overhead. Wenn zwei oder mehr abgeleitete Typen einer Schnittstelle vorhanden sind, verwenden Sie immer den Schnittstellentyp in Auflistungen / generischen Parametern.
Wenn Sie Thunkcode schreiben, dann gibt es wahrscheinlich irgendwo einen Konstruktionsfehler.
Wenn zum Zeitpunkt der Erfindung von IList<T>
Microsft gewusst hätte, dass zukünftige Versionen von .net Kovarianz und Kontravarianz der Schnittstelle unterstützen würden, wäre es möglich und sinnvoll gewesen, die Schnittstelle in IReadableList<out T>
,% co_de aufzuteilen %, und IAppendable<in T>
, die beide der oben genannten erben würden. Dies hätte den vb.net-Implementierern eine kleine Menge zusätzlicher Arbeit auferlegt (sie müssten sowohl die schreibgeschützte als auch die schreibgeschützte Version der indizierten Eigenschaft definieren, da .NET aus irgendeinem Grund kein Lesen-Schreiben zulässt Eigenschaft, die als schreibgeschützte Eigenschaft verwendet werden soll, würde aber bedeuten, dass Methoden, die einfach Elemente aus einer Liste lesen müssen, eine IList<T>
kovariant erhalten können und Methoden, die einfach eine Sammlung benötigen, an die sie anhängen können, erhalten IReadableList<T>
in kontravarianter Weise.
Leider wäre die einzige Möglichkeit, wie eine solche Sache heute implementiert werden könnte, wenn Microsoft ein Mittel bereitstellen würde, um neue Schnittstellen für ältere zu ersetzen, wobei Implementierungen der alten Schnittstellen automatisch Standardmethoden verwenden, die von den neuen bereitgestellt werden. Ich würde denken, dass ein solches Feature (Austauschbarkeit der Benutzeroberfläche) sehr hilfreich wäre, aber ich würde meinen Atem nicht anhalten und darauf warten, dass Microsoft es implementiert.
Da es keine Möglichkeit gibt, IAppendable<T>
in IReadableList<T>
einzufügen, wäre ein alternativer Ansatz, die eigene listenbezogene Schnittstelle zu definieren. Die einzige Schwierigkeit dabei ist, dass alle Instanzen von IList<T>
durch einen anderen Typ ersetzt werden müssen, obwohl die Schwierigkeit dies zu minimieren wäre, wenn man eine System.Collections.Generic.List<T>
struct in einem anderen Namespace definieren würde, der a enthält Ein einzelnes List<T>
-Feld und definierte Erweiterungskonvertierungen zum und vom Systemtyp (mit einer Struktur anstelle einer Klasse würde bedeuten, dass Code das Erstellen neuer Heap-Objekte beim Casting in einem Szenario vermeiden würde, in dem die Struktur nicht erforderlich wäre boxed).