简体   繁体   中英

Why can't a class with a type parameter used exclusively as an argument type be covariant on it?

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM