【C#补全计划】协变逆变
一、协变逆变的概念
1. 协变:子类型关系的正向继承
子类变父类 -> 感受是和谐的
2. 逆变:子类型关系的反向继承
父类变子类 -> 感受是不和谐的
3. 协变和逆变是用来修饰泛型的
(1)协变:out
(2)逆变:in
4. 只有泛型接口和泛型委托能使用
5. 是用来在泛型中修饰泛型字母的
二、协变逆变的作用
1. 限定返回值和参数
(1)用 out 修饰的泛型,只能作为返回值
(2)用 in 修饰的泛型,只能作为参数
using System;namespace OutAndIn
{// 1. 限制返回值与参数// 无限制:既可以作为返回值,也可以作为参数delegate T deTest1<T>();delegate void deTest2<T>(T t);// 用out修饰的泛型只能作为返回值// delegate T deOut<out T>(T t); 编译报错:因为使用T作为参数类型delegate T deOut<out T>();// 用in修饰的泛型只能作为参数// delegate T deIn<in T>(T t); 编译报错:因为使用T作为返回值类型delegate void deIn<in T>(T t);interface IOut<out T>{// void test(T t); 编译报错:因为使用T作为参数类型T test();}interface IIn<in T>{void test(T t);// T test(); 编译报错:因为使用T作为返回值类型}
}
2. 结合里氏替换原则使用
using System;namespace OutAndIn
{// 1. 限制返回值与参数// 无限制:既可以作为返回值,也可以作为参数delegate T deTest1<T>();delegate void deTest2<T>(T t);// 用out修饰的泛型只能作为返回值// delegate T deOut<out T>(T t); 编译报错:因为使用T作为参数类型delegate T deOut<out T>();// 用in修饰的泛型只能作为参数// delegate T deIn<in T>(T t); 编译报错:因为使用T作为返回值类型delegate void deIn<in T>(T t);interface IOut<out T>{// void test(T t); 编译报错:因为使用T作为参数类型T test();}interface IIn<in T>{void test(T t);// T test(); 编译报错:因为使用T作为返回值类型}// 2. 结合里氏替换原则class Father {}class Son : Father {}class Program{static void Main(string[] args){// 虽然ts和tf是不同的委托,但是泛型的类型是具有父子关系的// 无out修饰:deTest1<Son> ts1 = () => new Son();// deTest1<Father> tf1 = ts1; 编译报错:父类型委托不可以装载子类型委托// 有out修饰:deOut<Son> os = () =>{Console.WriteLine("调用out修饰的Son类型委托");return new Son();};deOut<Father> of = os; // 父类型委托可以装载子类型委托// 无in修饰:deTest2<Father> tf2 = (Father f) => { };// deTest2<Son> ts2 = tf2; 编译报错:子类型委托不可以装载父类型委托// 有in修饰:deIn<Father> iF = (Father f) =>{Console.WriteLine("调用in修饰的Father类型委托");};deIn<Son> iS = iF; // 子类型委托可以装载父类型委托Father f = of(); // 实际调用的是osiS(new Son()); // 实际调用的是iF}}
}
今天的学习就到这里了。感谢阅读。
再见!