https://www.cnblogs.com/springsnow/p/11652915.html#_label2
10分钟浅谈泛型协变与逆变 - 王柏成 - 博客园 (cnblogs.com)
C#4.0泛型的协变,逆变深入剖析 - 心出发 - 博客园 (cnblogs.com)
协变和逆变体现在泛型的接口和委托上面,也就是对泛型参数的声明,可以声明为协变,或者逆变。 如果有了参数的声明,则该泛型接口或者委托称为“变体”。
- “协变”是指能够使用与原始指定的派生类型相比,派生程度更大的类型。
- “逆变”则是指能够使用派生程度更小的类型。逆变,逆于常规的变。
协变和逆变,使用“out”,和“in”两个关键字。但是只能用在接口和委托上面,对泛型的类型进行声明。
协变
当类型参数声明为“out”时,代表它是用来返回的,只能作为结果返回,中途不能更改。
class Class1 { void Main() { ITest<Animal> customerList1 = new Test<Animal>(); ITest<Animal> customerList2 = new Test<Bird>();//这也是能编译的,在泛型中,子类指向父类,我们称为协变 //Net4.0中已有的实现 IEnumerable<Animal> list = new List<Bird>(); } }
/// <summary> /// 动物 /// </summary> public class Animal { public int Id { get; set; } }
/// <summary> /// 鸟 /// </summary> public class Bird : Animal { public string Name { get; set; } } /// <summary> /// 接口参数 out 协变, 只能是返回结果 /// </summary> /// <typeparam name="T"></typeparam> public interface ITest<out T> { //属性只能有 get 访问器 T Attr1 { get; } T Get(); // void Show(T t);//方法中T不能作为传入参数 } /// <summary> /// 注意:类没有协变逆变 /// </summary> /// <typeparam name="T"></typeparam> public class Test<T> : ITest<T> { public T Attr1 { get { return default(T); } } public T Get() { return default(T); } public void Show(T t) { } }
逆变
当类型参数声明为"in"时,代表它是用来输入的,只能作为参数输入,不能被返回。
class Class1 { void Main() { ITest<Bird> customerList1 = new Test<Bird>(); ITest<Bird> customerList2 = new Test<Animal>();//父类指向子类,我们称为逆变(强制转换) //Net4.0中已有的实现 Action<Bird> act = new Action<Animal>((Animal i) => { }); act(new Bird()); } } /// <summary> /// 动物 /// </summary> public class Animal { public int Id { get; set; } } /// <summary> /// 鸟 /// </summary> public class Bird : Animal { public string Name { get; set; } } /// <summary> /// 逆变 /// </summary> /// <typeparam name="T"></typeparam> public interface ITest<in T> { //T Get();//不能作为返回值 void Show(T t); } public class Test<T> : ITest<T> { public T Get() { return default(T); } public void Show(T t) { Console.Write(typeof(T).Name); } }