什么是协变 out 和逆变 in
首先将out in与 ref out这两个区分开来,ref out 是关于参数的引用传递的,
协变:out
和谐的变化,自然的变化
因为 里氏替换原则 父类可以装子类
所以 子类变父类
比如 string 变成 object
感受是和谐的
逆变:in
逆常规的变化,不正常的变化
因为 里氏替换原则 父类可以装子类 但是子类不能装父类(但是为什么子类确实在逆变里装了父类)
所以 父类变子类
比如 object 变成 string
感受是不和谐的
// 逆变其实也是从子类转成父类的,因为它其实是参数输出,子类容器放入的虽然是父类,但是调用时候的方法还是父类的,传入的参数是子类的,所以还是子类对象转化为父类
也可以简单理解为,in代表进局子了,不和谐,叛逆的,out代表出局子了,很和谐
知识点1
//1.返回值 和 参数
//用out修饰的泛型 只能作为返回值 协变
delegate T TestOut< out T>();
//用in修饰的泛型 只能作为参数,逆变
delegate void TestIn< in T>(T t);
using System;
namespace Lesson17_协变逆变
{
//协变和逆变是用来修饰泛型的
//协变:out
//逆变:in
//用于在泛型中 修饰 泛型字母的
//只有泛型接口和泛型委托能使用
#endregion
#region 知识点二 作用
//1.返回值 和 参数
//用out修饰的泛型 只能作为返回值,T不能当做参数传递 协变
delegate T TestOut<out T>();
//用in修饰的泛型 只能作为参数 ,T不能当做返回类型 逆变
delegate void TestIn<in T>(T t);
//2.结合里氏替换原则理解
class Father
{
}
class Son:Father
{
}
#endregion
class Program
{
static void Main(string[] args)
{
Console.WriteLine("协变逆变");
#region 知识点二 作用(结合里氏替换原则理解)
//协变 父类总是能被子类替换
// 看起来 就是 son ——> father
//这里是一个协变,所以不能有参数传递,只能返回Son对象
TestOut<Son> os = () =>
{
return new Son();
};
//上面返回的Son对象被父级Father的委托装起来了,这里可以理解为父类容器装的子类对象,注意如果
// delegate T TestOut<out T>(); 这里不加out ,那么下面这样写也会报错,加了out系统才会自动给你转换成父级
TestOut<Father> of = os;
Father f = of();//实际上 返回的 是os里面装的函数 返回的是Son
//逆变 父类总是能被子类替换
//看起来像是 father——>son 明明是传父类 但是你传子类 不和谐的
//这里是一个逆变,所以不能有返回值,传入的value也是Father类型的
//
TestIn<Father> iF = (value) =>
{
};
//这里iF其实就是父类委托,存入到了子类委托中,为什么能存进去,这不是违反了里式转换?看下面
TestIn<Son> iS = iF;
//这里只是存进去,并没有进行实质的子类转成父类,在去调用iS方法的时候,其实本质还是调用的iF,调用iS传入的参数必须是new Son(),其实就是 iF(new Son()),等于还是将Son转化为了Father,其实最后还是遵循了里式转换原则!!!
// 逆变其实也是从子类转成父类的,因为它其实是参数输出,子类容器放入的虽然是父类,但是调用时候的方法还是父类的,传入的参数是子类的,所以还是子类对象转化为父类
iS(new Son());//实际上 调用的是 iF ,delegate void TestIn< in T>(T t);所以必须要传入(new Son()
#endregion
}
}
//总结
//协变 out
//逆变 in
//用来修饰 泛型替代符的 只能修饰接口和委托中的泛型
//作用两点
//1.out修饰的泛型类型 只能作为返回值类型 in修饰的泛型类型 只能作为 参数类型
//2.遵循里氏替换原则的 用out和in修饰的 泛型委托 可以相互装载(有父子关系的泛型)
// 协变 父类泛型委托装子类泛型委托 逆变 子类泛型委托装父类泛型委托
}