为什么具有专门用作参数类型的类型参数的类不能对其进行协变?

时间:2016-06-26 21:27:47

标签: c# covariance

我发现的最常见的例子解释了为什么不能协同使用违反有效的类型参数,包括构造派生类型的一些读写包装器,将其转换为基类型的包装器,注入不同的派生类型,然后读取包裹的值;以下内容:

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.

我能理解为什么这是无效的。但是如果Wrapper没有pull,并且它包装的属性有一个私有的getter呢? (Wrapper<Base>)derived_wrapper强制转换的问题似乎消失了,我找不到替换它的问题。

更具体地说,我不知道任何方式的功能是以通用的最终具体类型为条件的。不出所料,断言如下所示类型是没有用的:

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'...
    }
  }
}

这让我相信这些方法中的功能只能使用T类型的属性受限制。即使包装属性是OtherDerived,其中原始WrapperImpl期望Derived(在投射之前),也没有方法可以期望包装属性具有derived_property,因为{{ 1}}是最具体的Base保证。

我在这里遗漏了什么,或者这可能是编译器无法动态地具体化T的一个限制?

(我猜测像T这样的课程很少使用,但是方差规则看起来相当宽泛,我很好奇是否有更精细的规则。)

1 个答案:

答案 0 :(得分:0)

T中的WrapperImpl<T>可以约束在 Base 的任何子类型上,而不仅仅是Base本身。如果put只是更改为v.derived_property,则T : Base中的功能应该能够安全地访问T : Derived

在尝试访问不存在的OtherDerived时,put(Wrapper<Base>)derived_wrapper投射后传递给derived_property时,这是一个麻烦的来源。