我很难理解这两个概念。但我认为在观看了许多视频和 SO QA 之后,我将其提炼为最简单的形式:
协变 - 假设子类型可以做其基本类型所做的事情。
逆变 - 假设您可以像对待其基本类型一样对待子类型。
假设这三个类:
class Animal
{
void Live(Animal animal)
{
//born!
}
void Die(Animal animal)
{
//dead!
}
}
class Cat : Animal
{
}
class Dog : Animal
{
}
协变
任何动物都可以做动物做的事。
假设一个子类型可以做它的基类型所做的事情。
Animal anAnimal = new Cat();
anAnimal.Live();
anAnimal.Die();
Animal anotherAnimal = new Dog();
anotherAnimal.Live();
anotherAnimal.Die();
逆变
任何你能对动物做的事,你都可以对任何动物做。
假设您可以像对待其基本类型一样对待子类型。
Action<Animal> kill = KillTheAnimal;
Cat aCat = new Cat();
KillTheCat(kill, aCat);
Dog = new Dog();
KillTheDog(kill, aDog);
KillTheCat(Action<Cat> action, Cat aCat)
{
action(aCat);
}
KillTheDog(Action<Dog> action, Dog aDog)
{
action(aDog);
}
void KillTheAnimal(Animal anAnimal)
{
anAnimal.Die();
}
这是正确的吗?似乎在一天结束时,协变和逆变允许你做的只是使用你自然期望的行为,即每种类型动物都具有所有动物特征,或者更笼统地说——所有子类型都实现了它们基类型的所有特性。似乎它只是允许显而易见的 - 它们只是支持不同的机制,允许您以不同的方式获得继承的行为 - 一种从子类型转换为基本类型(协方差),另一种从基本类型转换为子类型-type(逆变),但在其核心,两者都只允许调用基类的行为。
例如,在上面的例子中,您只是考虑了 Animal
的 Cat
和 Dog
子类型这两个事实有方法 Live
和 Die
- 它们很自然地继承自它们的基类 Animal
。
在这两种情况下 - 协变和逆变 - 我们都允许调用有保证的一般行为,因为我们已经确保调用行为的目标继承自特定基类。
在 Covariance 的情况下,我们将子类型隐式转换为它的基类型并调用基类型行为(如果基类型行为被覆盖则无关紧要)子类型...重点是,我们知道它存在)。
在逆变的情况下,我们采用子类型并将其传递给我们知道仅调用基类型行为的函数(因为基类型是形式参数类型),所以我们可以安全地将基本类型转换为子类型。
最佳答案
差异 - 指的是复杂类型(数组、列表、委托(delegate)、泛型)如何与其基础类型的子类型化方向相关。
换句话说,它是关于在什么方向上允许隐式转换复杂类型。
两个复杂类型(委托(delegate))根据其基础类型 Animal 和 Cat 的关系示例。
协方差 是关于子类型化方向(动物<-Cat)的隐式转换的保留方向
// Covariance based on type of return param of delegate
var catDelegate = new Func<Cat>(delegate {return null;});
// Allowed implicit casting from delegate based on Cat return param
// to delegate based on Animal return param
Func<Animal> animalDelegate = catDelegate;
逆变 是隐式转换相对于子类型方向的相反方向 (Animal->Cat)
// contravariance based on type of passed arguments of delegate
var animalDelegate = new Action<Animal>(delegate{});
// Allowed implicit casting from delegate based on Animal passed param
// to delegate based on Cat passed param
Action<Cat> catDelegate = animalDelegate;
不变性是一个不受支持的隐式转换(在任何方向)
通用列表是不变的
List<Animal> animals = new List<Cat>(); // error!
List<Cat> animals = new List<Animal>(); // error!
C# 中支持的变体示例
数组是协变的
Animal[] animals = new Cat[10]; // possible
泛型 IEnumerable 是协变的
IEnumerable<Animal> animals = new List<Cat>(); // possible
关于c# - 协变和逆变 - 调用有保证的基类行为的不同机制?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19262845/