Warum kann ich einen ListFoo nicht zurückgeben, wenn ich nach einem ListIFoo gefragt werde? [Duplikat]

7

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.

    
Joel in Gö 25.11.2008, 13:36
quelle

6 Antworten

14

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.

    
xtofl 25.11.2008, 13:45
quelle
12

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 .

    
Jon Skeet 25.11.2008 13:57
quelle
8

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%
    
Tom Ritter 25.11.2008 13:40
quelle
5

MASSIV BEARBEITEN Sie können es mit C # 4.0 machen, aber [danke Jon]

Sie können es umgehen mit ConvertAll :

%Vor%     
David Kemp 25.11.2008 14:25
quelle
0

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.

    
Skizz 25.11.2008 13:55
quelle
0

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).

    
supercat 03.07.2012 15:06
quelle

Tags und Links