Question

Écart générique en C # 4.0 a été mis en œuvre de telle sorte qu'il est possible d'écrire ce qui suit sans exception (qui est ce qui se passerait en C # 3.0):

 List<int> intList = new List<int>();
 List<object> objectList = intList; 

[Exemple non fonctionnel: Voir la réponse Jon Skeet]

J'ai récemment assisté à une conférence où Jon Skeet a donné un excellent aperçu de la variance générique, mais je ne suis pas sûr que je suis tout à fait l'obtenir - je comprends l'importance du in et out mots clés en matière de contre et co -variance, mais je suis curieux de ce qui se passe dans les coulisses.

Qu'est-ce que le CLR voir lorsque ce code est exécuté? Est-il convertir implicitement le List<int> à List<object> ou est-il simplement construit en ce que nous pouvons maintenant convertir entre les types dérivés de types de parents?

Hors intérêt, pourquoi pas introduit dans les versions précédentes et ce qui est le principal avantage - à savoir l'utilisation réelle du monde

Plus d'infos sur ce pour la variance générique (mais la question est extrêmement pas à jour, à la recherche de réel, des informations à jour)

Était-ce utile?

La solution

Non, votre exemple ne fonctionnerait pas pour trois raisons:

  • Classes (tels que List<T>) sont invariantes; seuls les délégués et les interfaces sont variante
  • Pour la variance de travailler, l'interface doit utiliser uniquement le paramètre de type dans un sens (en pour contravariance, pour covariance)
  • Les types de valeur ne sont pas pris en charge comme arguments de type pour la variance - donc il n'y a pas de converstion IEnumerable<int> à IEnumerable<object> par exemple

(Le code ne peut pas compiler en C # 3.0 et 4.0 -. Il n'y a pas exception)

Alors ce serait travail:

IEnumerable<string> strings = new List<string>();
IEnumerable<object> objects = strings;

Le CLR utilise uniquement la référence, sans changement - pas de nouveaux objets sont créés. Donc, si vous avez appelé objects.GetType() vous souhaitez toujours obtenir List<string>.

Je crois qu'il n'a pas été présenté plus tôt parce que les concepteurs de langage avaient encore travailler les détails sur la façon de l'exposer -. Il a été dans le CLR depuis v2

Les avantages sont les mêmes que d'autres fois où vous voulez être en mesure d'utiliser un type comme un autre. Pour utiliser le même exemple je samedi dernier, si vous avez quelque chose implémente IComparer<Shape> pour comparer les formes par région, il est fou que vous ne pouvez pas l'utiliser pour trier une List<Circle> - si l'on peut comparer deux formes, il peut certainement comparer les deux cercles. A partir de C # 4, il y aurait une conversion contravariante de IComparer<Shape> à IComparer<Circle>, vous pouvez l'appeler ainsi circles.Sort(areaComparer).

Autres conseils

Quelques réflexions supplémentaires.

  

Qu'est-ce que le voir CLR lorsque ce code est exécuté

Comme Jon et d'autres ont souligné à juste titre, nous ne faisons pas la variance sur les classes, seules les interfaces et les délégués. Donc, dans votre exemple, le voit CLR rien; que le code ne compile pas. Si vous forcez à compiler en insérant des moulages assez, il se bloque lors de l'exécution avec une mauvaise exception coulée.

Maintenant, il est encore une question raisonnable de se demander comment fonctionne la variance dans les coulisses quand il fonctionne. La réponse est: la raison pour laquelle nous limitons cette référence à des arguments de type qui paramétrisent interface et types est délégué pour que rien passe dans les coulisses. Quand vous dites

object x = "hello";

ce qui se passe dans les coulisses est la référence à la chaîne est bloquée dans la variable d'objet de type sans modification . Les bits qui composent une référence à une chaîne sont des bits juridiques à une référence à un objet, donc rien ne doit se passer ici. Le CLR cesse de simplement penser à ces bits comme se référant à une chaîne et commence à penser à eux comme faisant référence à un objet.

Quand vous dites:

IEnumerator<string> e1 = whatever;
IEnumerator<object> e2 = e1;

Même chose. Rien ne se passe. Les bits qui font une ref à un énumérateur de chaîne sont les mêmes que les bits qui font référence à un énumérateur de l'objet. Il y a un peu plus magique qui entre en jeu lorsque vous faites un casting, dites:

IEnumerator<string> e1 = whatever;
IEnumerator<object> e2 = (IEnumerator<object>)(object)e1;

Maintenant, le CLR doit générer un chèque e1 ne fait mettre en œuvre cette interface, et que l'enregistrement doit être intelligent de reconnaître la variance.

Mais la raison pour laquelle nous pouvons sortir avec des interfaces variantes étant tout simplement pas-op conversions est parce que compatibilité d'affectation régulière est de cette façon. Qu'est-ce que vous allez utiliser e2?

object z = e2.Current;

qui renvoie les bits qui sont une référence à une chaîne. Nous avons déjà établi que ceux-ci sont compatibles avec l'objet sans changement.

Pourquoi pas présenté plus tôt? Nous avions d'autres caractéristiques à faire et un budget limité.

Quel est le principal avantage? Cette conversion de séquence de chaîne à la séquence d'objet « juste ».

  

Hors intérêt, pourquoi était-ce pas   introduit dans les versions précédentes

Les premières versions (1.x) de .NET ne disposaient pas génériques du tout, donc la variance générique était loin.

Il convient de noter que dans toutes les versions de .NET, il y a covariance de tableau. Malheureusement, il est dangereux covariance:

Apple[] apples = new [] { apple1, apple2 };
Fruit[] fruit = apples;
fruit[1] = new Orange(); // Oh snap! Runtime exception! Can't store an orange in an array of apples!

La coopération et contre-variance en C # 4 est en sécurité et empêche ce problème.

  

quel est le principal avantage - à savoir réel   l'utilisation du monde?

Plusieurs fois dans le code, vous appelez une API de type On attend un amplifié de base (par exemple IEnumerable<Base>), mais tout ce que vous avez est un type de dérivé amplifié (par exemple IEnumerable<Derived>).

En C # 2 et C # 3, vous aurez besoin de convertir manuellement IEnumerable<Base>, même si elle devrait « juste ». Co et contre-variance rend « juste ».

p.s. succions totalement que la réponse de Skeet est en train de manger tous mes points de rep. Bon sang, Skeet! :-) On dirait qu'il est répondu à cette question, cependant.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top