C# 协变与逆变

“协变”是指能够使用与原始指定的派生类型相比,派生程度更大的类型。

“逆变”则是指能够使用派生程度更小的类型。

直白的理解:

“协变”->”和谐的变”->”很自然的变化”->string->object :协变。

“逆变”->”逆常的变”->”不正常的变化”->object->string 逆变。

如:

    // 支持协变的自定义接口
    public interface IOutDemo<out T> { }
    public class OutDemo<T> : IOutDemo<T>{}

    // 支持逆变的自定义接口
    public interface IInDemo<in T> { }
    public class InDemo<T> : IInDemo<T> { }

使用自定义的协变和逆变接口

            // Dog : Animal 即 Dog是Animal的子类

            OutDemo<Animal> outDemo =new  OutDemo<Dog>();// 错误,只有泛型接口或者泛型委托可以使用协变
            IOutDemo<Animal> _outDemo= new OutDemo<Dog>(); // 正确,泛型接口使用协变,Dog->Animal

            InDemo<Dog> inDemo = new InDemo<Animal>();// 错误,只有泛型接口或者泛型委托可以使用逆变
            IInDemo<Dog> _inDemo = new InDemo<Animal>(); //正确,,泛型接口使用逆变,Animal->Dog

 

C#协变和逆变只能用在泛型接口和泛型委托上

 协变(out)用在方法的返回值类型上,逆变(in)用在方法的参数类型上

    public interface IMyList<in T,out V>
    {
        // out 用于协变,限制该泛型接口参数只能作为接口方法的返回值类型
        V Get(T x);

        // in 用于逆变,限制该泛型接口参数只能作为接口方法的参数类型
        void Set(T x);

        //T Test(T x); // 错误, in T 限制了T只能作为方法参数类型
        //V Test(V x); // 错误, out V 限制了V只能作为方法返回值类型
    }

 

 个人理解:由于out限制了参数只能作为方法的返回值类型,则子类协变为父类就可以安全转换(因为总是可以隐式的将子类的实例赋值给父类型的变量);

同样由于in限制了参数只能作为方法的参数类型,意味着只能使用该参数,无法修改in参数的值,当把父类逆变为子类进行使用,由于子类和父类存在继承关系(即编译器内部可以使用显示强制转换,把父类型转换为子类型),子类继承了父类的资源,意味着显示转换后,你要使用父类的资源子类都继承了都具有该资源;

            Dog dog = new Dog();
            Animal animal = dog; // 总是可以将子类的实例赋值给父类型的变量(隐式转换)
            Dog dog1 = (Dog)animal;  // 需要显示转换(强制转换),因为存在继承关系所以可以强制转换成功

 

posted @ 2020-12-28 21:48  温故纳新  阅读(250)  评论(0编辑  收藏  举报