2016-06-26 3 views
0

L'exemple le plus commun que j'ai trouvé pour expliquer pourquoi les paramètres de type contravariantly-valides ne peuvent pas être utilisés covariantly impliquait la construction d'un wrapper en lecture-écriture d'un type dérivé, en l'injectant dans un wrapper puis lecture ultérieure de la valeur enveloppée; quelque chose comme suit:Pourquoi une classe avec un paramètre de type utilisé exclusivement comme type d'argument ne peut-elle pas être covariante?

class Base { public int base_property; } 
class Derived : Base { public int derived_property; } 
class OtherDerived : Base { public string other_derived_property; } 
interface Wrapper<out T> { 
    void put(T v); 
    T pull(); 
} 
Wrapper<Derived> derived_wrapper = new WrapperImpl<Derived>(); 
Wrapper<Base> cast_wrapper = (Wrapper<Base>)derived_wrapper; 
cast_wrapper.put(new OtherDerived()); 
Derived wrapped = derived_wrapper.pull(); // Surprise! This is an OtherDerived. 

Je peux comprendre pourquoi cela est invalide. Mais que se passe-t-il si Wrapper n'a pas pull et que la propriété qu'il contient a un getter privé? Le problème avec la distribution (Wrapper<Base>)derived_wrapper semble disparaître, et je ne peux pas trouver de problème pour le remplacer. Plus spécifiquement, je ne connais aucun moyen de conditionner la fonctionnalité au type concret éventuel d'un générique. Sans surprise, affirmant le type comme ce qui suit ne sert à rien:

class WrapperImpl<T> : Wrapper<T> where T : Base { 
    public void put(T v) { 
    if(typeof(T).Equals(Derived)) { 
     Console.WriteLine(v.derived_property); // Type `T' does not contain a 
              // definition for `derived_property'... 
    } 
    } 
} 

Cela me porte à croire que la fonctionnalité de ces méthodes ne peut utiliser que des propriétés du type T est contraint à. Même si la propriété enveloppée est un OtherDerived où l'original WrapperImpl s'attendait à Derived (avant d'être casté), aucune méthode ne pouvait s'attendre à ce que la propriété enveloppée ait derived_property car Base est la plus spécifique T est garanti être. Ai-je manqué quelque chose ici, ou est-ce une limitation du compilateur pour ne pas pouvoir concrétiser T à la volée?

(je devine qu'une classe comme Wrapper trouve quelques utilisations, mais les règles de la variance semblent assez large et de balayage, et je suis curieux de savoir s'il y a des règles plus fines en jeu.)

Répondre

0

Le T en WrapperImpl<T> peut être contraint sur tout sous-type de Base, pas seulement Base lui-même. Fonctionnalité dans put devrait être en mesure d'accéder en toute sécurité v.derived_property si T : Base est simplement changé en T : Derived.

C'est la source de problèmes lorsqu'un OtherDerived est passé à put après le casting (Wrapper<Base>)derived_wrapper, en essayant d'accéder au derived_property inexistant.