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.)