Kann kein ListFoo an eine Methode übergeben, die ein ListIFoo erwartet, wobei Foo: IFoo

7

Ich habe eine Klasse Foo , die die IFoo Schnittstelle implementiert. Ich habe eine Methode, die einen List<IFoo> als Parameter verwendet. Es kann jedoch nicht von List<Foo> in List<IFoo> konvertiert werden - das überrascht mich, da Foo das Interface IFoo implementiert.

Wie kann ich das umgehen, und warum passiert das? (Immer gut aus Fehlern lernen)

    
The Communist Duck 01.08.2011, 17:34
quelle

5 Antworten

12

Dies liegt daran, dass List<T> nicht kovariant ist. Weitere Informationen finden Sie unter Kovarianz und Kontravarianz in C # .

Wenn Sie Ihre Methode stattdessen auf IEnumerable<T> anwenden können, funktioniert dies (Sie können List<Foo> in IEnumerable<IFoo> in .NET 4 übergeben, da IEnumerable<out T> definiert ist).

Der Grund, warum List<T> nicht kovariant ist, liegt daran, dass es keinen schreibgeschützten Vertrag definiert. Da List<T> explizit das Hinzufügen von Elementen erlaubt (via Add(T) ), ist es nicht sicher, die Kovarianz zu erlauben. Wenn dies erlaubt wäre, würde die Methode erwarten, ein Element vom Typ Bar (wenn Bar von IFoo abgeleitet wird) zur Liste hinzuzufügen, aber das würde fehlschlagen, da die Liste wirklich a List<Foo> , nicht List<IFoo> . Da IEnumerable<T> nur erlaubt, die Liste zu durchlaufen, aber nicht zu modifizieren, kann sie kovariant sein und in diesem Fall einfach wie erwartet funktionieren.

    
Reed Copsey 01.08.2011, 17:37
quelle
8

Glauben Sie es oder nicht, das ist nicht typsicher.

Betrachten Sie den folgenden Code:

%Vor%

Sie fragen nach einer kovarianten Konvertierung; das ist nur für unveränderliche Typen möglich.
Außerdem unterstützt .Net nur die Kovarianz für Schnittstellen.

Ändere die Methode, um ein IEnumerable<IFoo> zu nehmen und es wird funktionieren.

    
SLaks 01.08.2011 17:37
quelle
2

Denken Sie daran, dass es keine Vererbungs- oder Implementierungsbeziehung zwischen Spezialisierungen eines generischen Typs gibt. List<Foo> erbt in keiner Weise von List<IFoo> . Es ist vergleichbar mit string bis int .

Das bedeutet nicht, dass überhaupt keine Beziehung zwischen den Typen besteht; nur dass die betreffende Beziehung keine Vererbung ist. Stattdessen haben wir eine Spezialisierung Beziehung zwischen den beiden Typen. Bestimmte Typen in .Net 4.0 und höher unterstützen ein Attribut der Spezialisierung, das als Kovarianz bekannt ist. Co- und Kontravarianz ermöglichen es, dass der spezialisierte Teil eines generischen Typs variiert (beachten Sie das Wort dort) unter bestimmten Umständen. List<T> gehört nicht zu diesen Typen.

Aber was ich in diesem Fall tun würde (wo ich nicht sicher bin, dass Sie .Net 4.0 haben) ist, die ganze Methode generisch zu machen:

%Vor%

Beachten Sie, dass ich auch IEnumerable<T> anstelle von List<T> verwendet habe. Sie sollten weiterhin Listen an diese Funktion übergeben können, da eine Vererbungs- / Implementierungsbeziehung zwischen IEnumerable<T> und List<T> besteht. Sie können auch Arrays und jede andere unterstützte Sequenz verarbeiten. IEnumerable ist auch einer der Typen, die die Kovarianz unterstützen. Fast jede Methode, die ein Argument List<T> benötigt, sollte aktualisiert werden, um stattdessen IEnumerable<T> zu verwenden.

    
Joel Coehoorn 01.08.2011 17:39
quelle
0

Sie müssen ein neues List<IFoo> erstellen und alle Elemente aus der ersten Liste hinzufügen.

    
Dark Falcon 01.08.2011 17:37
quelle
0

Nun, Sie können es in eine neue Liste konvertieren, die als List<IFoo> deklariert ist, was Sie so brauchen:

%Vor%

Das wird funktionieren, denke ich ... weil Foo nach IFoo castbar ist, und ToList gibt eine neue Liste mit dem Typ specefied zurück.

    
Cipi 01.08.2011 17:42
quelle

Tags und Links