一、协变逆变的概念
- 协变:子类型关系的正向继承
子类变父类 -> 感受是和谐的
- 逆变:子类型关系的反向继承
父类变子类 -> 感受是不和谐的
- 协变和逆变是用来修饰泛型的
(1)协变:out
(2)逆变:in
-
只有泛型接口和泛型委托能使用
-
是用来在泛型中修饰泛型字母的
二、协变逆变的作用
- 限定返回值和参数
(1)用 out 修饰的泛型,只能作为返回值
(2)用 in 修饰的泛型,只能作为参数
cs
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作为返回值类型
}
}
- 结合里氏替换原则使用
cs
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(); // 实际调用的是os
iS(new Son()); // 实际调用的是iF
}
}
}
今天的学习就到这里了。感谢阅读。
再见!