Comment la covariance générique et la contre-variance sont-elles implémentées dans C # 4.0?

106

Je n'ai pas assisté à PDC 2008, mais j'ai entendu des nouvelles selon lesquelles C # 4.0 est annoncé pour prendre en charge la covariance et la contra-variance génériques. Autrement dit, List<string>peut être attribué à List<object>. Comment cela pourrait-il être?

Dans le livre C # in Depth de Jon Skeet , il est expliqué pourquoi les génériques C # ne prennent pas en charge la covariance et la contre-variance. C'est principalement pour écrire du code sécurisé. Maintenant, C # 4.0 a changé pour les prendre en charge. Cela apporterait-il le chaos?

Quelqu'un connaît les détails sur C # 4.0 peut donner quelques explications?

Morgan Cheng
la source
Voici un bon article qui couvre les prochaines implémentations de covariance et de contra-variance sur les délégués et les interfaces en C # 4.0: Ferme LINQ: Covariance et Contravariance en C # 4.0
CMS
Anders Noråse explique en C # 4.0 - Covariance et contra-variance le concept et montre qu'il est déjà pris en charge aujourd'hui en IL depuis .NET 2.0.
Thomas Freudenberg

Réponses:

155

La variance ne sera prise en charge que de manière sûre - en fait, en utilisant les capacités que le CLR possède déjà. Ainsi, les exemples que je donne dans le livre pour essayer d'utiliser un List<Banana>comme un List<Fruit>(ou quoi que ce soit) ne fonctionneront toujours pas - mais quelques autres scénarios le seront.

Premièrement, il ne sera pris en charge que pour les interfaces et les délégués.

Deuxièmement, il faut que l'auteur de l'interface / du délégué décore les paramètres de type comme in(pour contravariance) ou out(pour covariance). L'exemple le plus évident est celui IEnumerable<T>qui ne vous permet que d'en "retirer" des valeurs - cela ne vous permet pas d'en ajouter de nouvelles. Cela deviendra IEnumerable<out T>. Cela ne nuit pas du tout à la sécurité des types, mais vous permet de renvoyer un à IEnumerable<string>partir d'une méthode déclarée à renvoyer IEnumerable<object>par exemple.

La contravariance est plus difficile à donner des exemples concrets d'utilisation d'interfaces, mais c'est facile avec un délégué. Considérez Action<T>- cela représente juste une méthode qui prend un Tparamètre. Ce serait bien de pouvoir convertir de manière transparente en utilisant un Action<object>comme Action<string>- toute méthode qui prend un objectparamètre fonctionnera bien lorsqu'elle sera présentée avec un à la stringplace. Bien sûr, C # 2 a déjà une covariance et une contravariance des délégués dans une certaine mesure, mais via une conversion réelle d'un type de délégué à un autre (création d'une nouvelle instance) - voir P141-144 pour des exemples. C # 4 rendra cela plus générique et (je crois) évitera de créer une nouvelle instance pour la conversion. (Ce sera plutôt une conversion de référence.)

J'espère que cela clarifie un peu les choses - s'il vous plaît laissez-moi savoir si cela n'a pas de sens!

Jon Skeet
la source
3
Donc, cela signifie-t-il que si la classe est déclarée comme "List <out T>", alors elle ne devrait PAS avoir de fonction membre comme "void Add (T obj)"? Le compilateur C # 4.0 signalera une erreur à ce sujet, non?
Morgan Cheng
1
Morgan: C'est certainement ce que je comprends, oui.
Jon Skeet
4
encore une fois, une de vos réponses ici sur SO m'a immédiatement aidé à améliorer certains codes. Je vous remercie!
Marquez
@ Ark-kun: Oui, j'en suis conscient. D'où le "toujours ne fonctionnera pas" dans la même phrase. (Et je connais aussi les raisons.)
Jon Skeet
@JonSkeet Est-il exact que "vous ne pouvez utiliser un " que List<Banana>comme un IList<Fruit>"comme @ Ark-kun l'a dit? Si tel est le cas, comment est-ce possible, bien que le paramètre de type de l' IList<T>interface ne soit pas défini comme covariant (non out T, mais simplement T).
gehho