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; // 需要显示转换(强制转换),因为存在继承关系所以可以强制转换成功