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)
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 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.
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.
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.
Sie müssen ein neues List<IFoo>
erstellen und alle Elemente aus der ersten Liste hinzufügen.