MSDN定义:在 C# 和 Visual Basic 中,协变和逆变允许数组类型、委托类型和泛型类型参数进行隐式引用转换。 协变保留分配兼容性,逆变与之相反。从基类到派生类,派生程度越来越高,也就是说,基类的派生程度最低。

协变

delegate T Factory<out T>();//这里的out告诉编译器,类型参数T用于返回值

class Animal
{
    public int legs = 4;
}

class Dog : Animal { }

class Program
{
    static void Main(string[] args)
    {
        Factory<Dog> dogMaker = MakeDog;
        /* 这里发生了协变,即将返回派生类的方法赋值给了返回基类的委托。
         * 理解:将返回派生类的方法dogMaker复制给返回基类的委托animalMaker,这样是可行的。
         * 因为当别的地方调用该委托时,希望得到的是一个Animal类型的返回值,而dogMaker的返
         * 回值类型是Dog,因为Dog派生自Animal,由于可以把派生类对象赋值给基类引用,所以编
         * 译器允许这种赋值,我们把这种赋值叫做协变。同时,为了确认这是编程人员想要的,编译器
         * 需要编程人员明确的用out关键字标识类型参数T。
         */
        Factory<Animal> animalMaker = dogMaker;
    }

    static Dog MakeDog()
    {
        return new Dog();
    }
}

逆变

class Animal
{
    public int legs = 4;
}

class Dog : Animal { }

class Program
{
    delegate void Action<in T>(T a);

    static void ActOnAnimal(Animal a)
    {
        Console.WriteLine(a.legs);
    }

    static void Main(string[] args)
    {
        Action<Animal> act = ActOnAnimal;
        /* 这里发生了逆变,即将期望传入基类参数的方法,赋值给期望传入派生类的委托。
         * 理解:ActOnAnimal方法期望处理Animal类型的参数,那么将其赋值给Action<Dog>
         * 类型的委托dog,因为dog接收Dog类型,而能够处理Animal的方法,当然能够正确的处
         * 理其派生类型(Dog)。
         * 
         * 反过来,如果将期望处理派生类型的方法Bar,赋值给期望处理基类型的委托Foo,那么在
         * 调用委托时,如果将基类对象作为参数传入,Bar不一定能够正确运行,因为基类可能并不
         * 具备派生类的某些特性。
         */
        Action<Dog> dog = act;
        dog(new Dog());
    }
}

协变说的是返回值,可以返回派生类型的值。
即对于泛型委托,可以将返回值类型派生程度高(子孙备份小)的委托,赋值给返回值类型派生程度低的委托。

逆变说的是输入值,只能输入基类对象。
即对于泛型委托,可以将输入值类型派生程度低的委托,赋值给输入值类型派生程度高的委托。

发表评论

电子邮件地址不会被公开。 必填项已用*标注