你说得对,最后一条规则是最难理解的,但我向你保证它并不含糊。
一两个例子会有帮助。考虑这个类型声明:
interface I<in T, out U, V> { ... }
是这个类型吗协变有效?
I<string, object, int> { }
让我们看一下我们的定义。
为了确定是否是,我们以不同的方式检查每个类型参数,具体取决于相应的类型参数是否被声明为协变(out)、逆变(in)或不变(两者都不是)。
好的,所以类型参数是string
, object
and int
。对应的参数为in T
, out U
and V
, 分别。
如果第 i 个类型参数被声明为协变 (out
),那么它必须是协变有效的。
第二个类型参数是out U
, so object
必须是协变有效的。这是。
如果它被声明为逆变(in
),那么 Ti 必定是逆变有效的。
第一个被宣布了in T
, so string
必须是逆变有效的。这是。
如果它被声明为不变式,那么 Ti 必定是有效的不变式。
第三V
是不变的,所以int
必须是不变有效的;它必须同时有效逆变和协变。这是。
我们通过了所有三项检查;方式I<string, object, int>
是协变有效的。
好吧,那很简单。
现在让我们看一个更难的。
interface IEnumerable<out W> { ... }
interface I<in T, out U, V>
{
IEnumerable<T> M();
}
IEnumerable<T>
inside I
是一种类型。是IEnumerable<T>
正如里面所使用的I
协变有效?
让我们看一下我们的定义。我们有类型参数T
对应类型参数out W
。注意T
是一种类型范围 of I
和一个类型argument of IEnumerable
.
如果第 i 个类型参数 (W
) 被声明为协变 (out
), 那么 Ti (T
) 必须是协变有效的。
好的,所以对于IEnumerable<T>
in I
协变有效,T
必须是协变有效的。是吗?不。T
被宣布为in T
。声明的类型参数in
永远不会是协变有效的。因此类型IEnumerable<T>
正如里面所使用的I
协变无效,因为违反了“必须”条件。
再说一遍,就像我在回答你之前的问题时所说的那样,如果“有效协变”和“有效逆变”让你感到悲伤,只需给它们起不同的名字即可。它们是明确定义的形式属性;如果您更容易理解,您可以随意称呼它们。