The most common example I have found to explain why contravariantly-valid type parameters cannot be used covariantly has involved constructing some read-write wrapper of a derived type, casting it to a wrapper of the base type, injecting a different derived type, then later reading that wrapped value ; something along the lines of the following:
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.
I can understand why this is invalid. But what if Wrapper
didn't have pull
, and the property it wraps has a private getter? The problem with the (Wrapper<Base>)derived_wrapper
cast seems to disappear, and I can't find a problem to replace it.
More specifically, I'm not aware of any way for functionality to be conditional on the eventual concrete type of a generic. Unsurprisingly, asserting the type like the following is no use:
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'...
}
}
}
This leads me to believe that functionality in these methods can only make use of properties of the type T
is constrained to. Even if the wrapped property is an OtherDerived
where the original WrapperImpl
expected a Derived
(before being cast), no method could expect that wrapped property to have derived_property
because Base
is the most specific T
is guaranteed to be.
Am I missing something here, or perhaps is this a limitation of the compiler to not be able to concretize T
on the fly?
(I'm guessing that a class like Wrapper
finds few uses, but the rules of variance appear quite broad and sweeping, and I'm curious if there are finer rules in play.)
The T
in WrapperImpl<T>
can be constrained on any subtype of Base
, not just Base
itself. Functionality in put
should be able to safely access v.derived_property
if T : Base
is simply changed to a T : Derived
.
This is the source of trouble when an OtherDerived
is passed to put
after the (Wrapper<Base>)derived_wrapper
cast, in trying to access the nonexistent derived_property
.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.