我发现的最常见的例子解释了为什么不能协同使用违反有效的类型参数,包括构造派生类型的一些读写包装器,将其转换为基类型的包装器,注入不同的派生类型,然后读取包裹的值;以下内容:
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
这样的课程很少使用,但是方差规则看起来相当宽泛,我很好奇是否有更精细的规则。)
答案 0 :(得分:0)
T
中的WrapperImpl<T>
可以约束在 Base
的任何子类型上,而不仅仅是Base
本身。如果put
只是更改为v.derived_property
,则T : Base
中的功能应该能够安全地访问T : Derived
。
在尝试访问不存在的OtherDerived
时,put
在(Wrapper<Base>)derived_wrapper
投射后传递给derived_property
时,这是一个麻烦的来源。