五分钟看完,彻底理解协变逆变 - BruceNeter - 博客园

来源: 五分钟看完,彻底理解协变逆变 – BruceNeter – 博客园

其实这是C#的老知识点了,但是今天发现同事对这个竟然还一知半解,就和他们讲解了下,顺便也回顾了下,同事我也把我对这个的全部理解,融化成几分钟的讲解,保证大家5分钟内全部理解,看不懂来打我。

 

协变、逆变 解决的问题

泛型类型转换

比如Person类是Student的父类,我们平时可以直接:

Person A = new Student();

这是所谓的隐式转换,相信百分之999.99%的人都知道。
然后随着大家写代码越来越多,就会遇到这样的场景。

//我有一个集合
//我手上有一批学生
IEnumerable<Student> students = new List<Student>();
//我要他们先做人
IEnumerable<Person> peoples = students;

第一次看到这种代码,其实哪怕你一点不知道协变,逆变,你也觉得这是一段正常不过的代码,因为每个学生都是人,都可以直接转成 这个类型,那我一批学生不就是一批人吗。是的,你这样想绝对没错,不然微软怎么会能让你这样写没问题还编译通过呢?
但是如果我自己写一个:

//定义一个工作的泛型接口
public interface IWork<T> 
{
            
}

实现类
public class Work<T> : IWork<T> 
{
            
}

//直接报错
IWork<Person> work = new Work<Student>();;

现实给了我们当头一棒,这时候,我们应该找到 IEnumerable,选中然后狠狠的F12去看一下,为什么官方的就可以。

我们发现官方在泛型前面多了一个out关键字。破案了~
现在我们在我们的代码中也加入out关键字

public interface IWork<out T> 
{
            
}

public class Work<T> : IWork<T> 
{
            
}
IWork<Person> work = new Work<Student>();

OK~代码正常运行。

原则核心

这里开始我们挑战五分钟速通,如果按照正常博客上来先讲概念,别说五分钟了,可能大家也就迷迷糊糊地看完了,所以我们直接整活。

核心依据

正如数学的发展是从1+1=2作为开始,我们也需要一些真理来支撑我们讲下去。那么我们的核心依据就是:
里氏替换——C#里,子类转父类可以直接隐式转换
就这么短,就完事了?对,记住就行!!!

Out/In 输入输出?

讲到这里,我们继续忽悠,out是啥?来个翻译!不就是输出吗?in是啥,不就是输入吗?那么带入一下,Out不就是返回值吗,In不就是入参吗。那不就是方法的特征么。(先假设,再假设)

In:那么根据核心依据,子类转父类可以直接转,入参如果限定是Person类型,那么你给我限定为Student或者任意的Person类型的派生类,我都是可以接受的,因为都是安全的,可以直接转换过来的。

这种从基类转向派生类的兼容,就是所谓的逆变。
说白了,我让你给我一个人,你说不行,我给你找个学生,那肯定是满足需求的。

Out:Out代表的是返回值,根据核心依据,我返回的是Student类型,你说不行,你给我返回Person类型,那我不是笑开花了,我连Student都能返回,你让我返回父类,那我不是直接转就过去了,总归是类型安全的。

这种从派生类转向基类的兼容,就是所谓的协变。
说白了,我可以造个学生,结果你说给个人就行, 那不是so easy。

In示意图

Out示意图

证明

好了,我们说了这么多,至少证明下In/Out是代表的入参和返回值吧?直接show you code:
当Out作为返回值时的泛型没有问题,但是入参就报错了

当In作为入参时的泛型没有问题,但是返回值就报错了

好了,这还需要再解释吗?最后我们总结下,逆变和协变就是让方法有了泛型类型上的转换能力,强化了方法的多态能力。

问题点

1.属性为啥可以用逆变协变?
属性不就是get/set方法。
2.为什么接口和委托可以用逆变协变,类不行?
拜托你找一下共同点,接口和委托的共同点,都是行为,也就是方法为核心。接口里不能有字段。这也印证了我说的逆变协变最终是为方法服务的。
之所以类不行,我大概理解是方法和实例是分开的,本身不和实例存储在一起,也不是每个实例一份,如果逆变和协变可以服务类,那么会出现同样的类型,但是每个实例内部的同一个字段的类型都不一样,这对于存储和类型安全都是问题。
3.逆变和协变有啥用?
当你…设计问题,我就有遇到,有时候用上能更加优雅或者灵活的写代码吧,看你吧,少年。

赞(0) 打赏
分享到: 更多 (0)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏