逆变的定义如下。一张地图F
从类型到类型的映射T
to F<T>
是逆变的T
如果每当U
and V
是类型,使得每个类型的对象U
可以分配给类型的变量V
, 每个类型的对象F<V>
可以分配给类型的变量F<U>
(F
反转赋值兼容性)。
特别是,如果T -> IComparer<T>
然后注意类型变量IComparer<Derived>
可以接收一个对象实现IComparer<Base>
。这就是逆变。
我们这么说的原因IComparer<T>
是逆变的T
是因为你可以说
class SomeAnimalComparer : IComparer<Animal> { // details elided }
进而:
IComparer<Cat> catComparer = new SomeAnimalComparer();
编辑:你说:
我明白了什么是逆变和协变。我问为什么那些比较接口在默认情况下被更改为逆变。
改变了?我是说,IComparer<T>
是“自然”逆变的。的定义IComparer<T>
is:
public interface IComparer<T> {
int Compare(T x, T y);
}
注意T
仅出现在该界面的“in”位置。也就是说,没有返回实例的方法T
. Any这样的接口“自然”是逆变的T
.
鉴于此,您有什么理由不想使其逆变呢?如果您有一个知道如何比较实例的对象U
, and V
赋值是否兼容U
,为什么你不应该能够将此对象视为知道如何比较实例的对象V
?这就是逆变所允许的。
在逆变之前,您必须换行:
class ContravarianceWrapperForIComparer<U, V> : IComparer<V> where V : U {
private readonly IComparer<U> comparer;
public ContravarianceWrapperForIComparer(IComparer<U> comparer) {
this.comparer = comparer;
}
public int Compare(V x, V y) { return this.comparer.Compare(x, y); }
}
然后你可以说
class SomeUComparer : IComparer<U> { // details elided }
IComparer<U> someUComparer = new SomeUComparer();
IComparer<V> vComparer =
new ContravarianceWrapperForIComparer<U, V>(someUComparer);
逆变允许您跳过这些咒语,只需说
IComparer<V> vComparer = someUComparer;
当然,以上只是当V : U
。通过逆变,您可以随时执行此操作U
赋值是否兼容V
.