Wie wird generische Kovarianz und Kontravarianz in C # 4.0 implementiert?

106

Ich habe nicht an der PDC 2008 teilgenommen, aber ich habe einige Neuigkeiten gehört, dass C # 4.0 angekündigt wurde, um generische Kovarianz und Kontravarianz zu unterstützen. Das heißt, List<string>kann zugewiesen werden List<object>. Wie kann das sein?

In Jon Skeets Buch C # in Depth wird erklärt, warum C # -Generika Kovarianz und Kontravarianz nicht unterstützen. Es dient hauptsächlich zum Schreiben von sicherem Code. Jetzt wurde C # 4.0 geändert, um sie zu unterstützen. Würde es Chaos bringen?

Weiß jemand, dass die Details zu C # 4.0 eine Erklärung geben können?

Morgan Cheng
quelle
Hier ist ein guter Artikel, der die bevorstehenden Kovarianz- und Kontravarianzimplementierungen für Delegaten und Schnittstellen in C # 4.0 behandelt: LINQ Farm: Kovarianz und Kontravarianz in C # 4.0
CMS
Anders Noråse erklärt in C # 4.0 - Kovarianz und Kontravarianz das Konzept und zeigt, dass es in IL bereits seit .NET 2.0 unterstützt wird.
Thomas Freudenberg

Antworten:

155

Varianz wird nur auf sichere Weise unterstützt - tatsächlich unter Verwendung der Fähigkeiten, über die die CLR bereits verfügt. Die Beispiele, die ich in dem Buch gebe, in dem versucht wird, a List<Banana>als List<Fruit>(oder was auch immer es war) zu verwenden, funktionieren also immer noch nicht - aber einige andere Szenarien werden es tun.

Erstens wird es nur für Schnittstellen und Delegaten unterstützt.

Zweitens muss der Autor der Schnittstelle / des Delegaten die Typparameter als in(für Kontravarianz) oder out(für Kovarianz) dekorieren . Das offensichtlichste Beispiel ist, IEnumerable<T>dass Sie nur Werte "herausnehmen" können - Sie können keine neuen hinzufügen. Das wird werden IEnumerable<out T>. Das schadet der Typensicherheit überhaupt nicht, aber Sie können beispielsweise IEnumerable<string>eine Methode zurückgeben, die als zurückgegeben deklariert wurde IEnumerable<object>.

Kontravarianz ist schwieriger, konkrete Beispiele für die Verwendung von Schnittstellen zu nennen, aber mit einem Delegierten ist es einfach. Bedenken Sie, Action<T>dass dies nur eine Methode darstellt, die einen TParameter akzeptiert. Es wäre schön, wenn Sie a Action<object>as nahtlos konvertieren Action<string>könnten - jede Methode, die einen objectParameter verwendet, ist in Ordnung, wenn sie mit a dargestellt wirdstring , statt. Natürlich weist C # 2 bereits eine gewisse Kovarianz und Kontravarianz von Delegaten auf, jedoch über eine tatsächliche Konvertierung von einem Delegatentyp in einen anderen (Erstellen einer neuen Instanz) - Beispiele finden Sie auf P141-144. C # 4 wird dies allgemeiner machen und (glaube ich) vermeiden, eine neue Instanz für die Konvertierung zu erstellen. (Es wird stattdessen eine Referenzkonvertierung sein.)

Hoffe das klärt es ein bisschen auf - bitte lass es mich wissen, wenn es keinen Sinn ergibt!

Jon Skeet
quelle
3
Bedeutet dies also, dass die Klasse, wenn sie als "List <out T>" deklariert ist, KEINE Mitgliedsfunktion wie "void Add (T obj)" haben sollte? Der C # 4.0-Compiler meldet diesbezüglich einen Fehler, oder?
Morgan Cheng
1
Morgan: Das ist sicherlich mein Verständnis, ja.
Jon Skeet
4
Wieder einmal hat mir eine Ihrer Antworten hier auf SO sofort geholfen, Code zu verbessern. Danke dir!
Mark
@ Ark-Kun: Ja, das ist mir bewusst. Daher wird das "immer noch nicht funktionieren" im selben Satz. (Und ich bin mir auch der Gründe bewusst.)
Jon Skeet
@ JonSkeet Ist es richtig, dass "Sie nur ein List<Banana>als verwenden können IList<Fruit>", wie @ Ark-kun sagte? Wenn ja, wie ist dies möglich, obwohl der Typparameter der IList<T>Schnittstelle nicht als kovariant definiert ist (nein out T, sondern einfach T).
gehho