环境:

  • window 10
  • .netcore 3.1
  • vs2019 16.5.1

一、为什么要有协变?

首先看下面的代码:
c#: 协变和逆变深度解析
还有下面的:
c#: 协变和逆变深度解析
其实上面报错的是同一个问题,就是你无法用List<Fruit>指向List<Apple>
我们的疑问在于,明明是一个盛放苹果的箱子,我们说它可以盛放水果怎么了???
下面我来说一下原因:

  • 首先,不能根据这个类的用途去判断,因为你无法保证List这个类一定是集合(List当然是集合,但如果是Person<T>呢,它是做什么的?只是盛放东西吗?)。
  • 其次,Apple继承自Fruit没错,但List<Apple>List<Fruit>压根就没有继承的说法,它们是不同的类型(泛型参数类型不同也是不同的类型):

    Console.WriteLine(typeof(ITest<Apple>) == typeof(ITest<Fruit>));输出为:false

所以,我们用List<Fruit>去表示List<Apple>引发报错很正常!!!

但是,从我们程序员角度来说,这样肯定不方便,那么有没有解决办法呢?
答案:有,它就是协变!

二、什么是协变?

首先,明确一下目的:我们想让List<Fruit> list = new List<Apple>();这类代码成立!(这行代码肯定不成立,我说的是这类代码)
想要达到我们的目的,肯定是要有规则的:

  • 必须使用接口进行指向,不能使用类:

    比如说:我们只能这么写IList<Fruit> list = new List<Apple>();(虽然这样写也报错),不能够这么写List<Fruit> list = new List<Apple>();
    为什么不能使用类?因为类里面牵扯到的东西比较多,而接口里面比较简单,所以只在接口上做规则就行了(这一点是猜的)。

  • 这个接口的泛型参数只能用来做接口内方法的返回值,不能用作接口内方法的参数(在泛型参数前加out关键字实现):

    这里从两方面说:
    1.允许这个泛型参数做返回值:Fruit可以作为方法的返回值,而实际上人家返回的就是Fruit的子类Apple,所以一点问题没有。
    2.禁止这个泛型参数做方法的入参:假如泛型参数做方法的入参,调用者看到这个方法接收的参数可以是Fruit,那么调用者就有可能传一个
    orange(橙子,也集成了Fruit)进去,而人家实际上是一个IList<Apple>,这样肯定说不通!所以泛型参数禁止做方法的入参!

上面说了规则,那么下面来一个实例:
c#: 协变和逆变深度解析
可以看到,我们按照规则在ITest的泛型参数T上加了out后,整个程序腰部酸了、腿不疼了。
事实上,微软已经在集合上的定义上已经考虑到了这一点,看一下IEnumerable的定义:
c#: 协变和逆变深度解析
所以,我们向下面这样写也没有错:
c#: 协变和逆变深度解析
讲到这里,我们可以说一下什么是协变了:
假如有两个类:AAA,其中AA集成子A,如果此时有一个泛型接口IC<out T>,那么可以认为IC<A>能指向IC<AA>

三、什么是逆变?

如果你理解了上面说的什么是协变,那么我说:逆变和协变是相对的,你一定也能理解。
具体来说:
逆变的目的是:让List<Apple> test = new List<Fruit>();这类代码成立!(这行代码肯定报错,我说的是这类代码)
你一定认为这肯定疯了,“说一个盛放水果的箱子盛放的是苹果”肯定不对。
上面那个代码肯定不成立,但是我们看下面的实例:
c#: 协变和逆变深度解析
上图中的实例是不是颠覆了你的认知?
好吧,这就是协变:一个可以让你用ITest<Apple>去指向Test<Fruit>()的存在!
这里还在再说一下协变的规则:

  • 必须使用接口进行指向,不能使用类:

    这一点和协变是一样的。

  • 这个接口的泛型参数只能用来做接口内方法的入参,不能用作接口内方法的返回值(在泛型参数前加in关键字实现):

    这里从两方面说:
    1.允许这个泛型参数做入参:Apple作为接口内方法的入参,那么调用者实际传入的是Fruit,一点问题没有。
    2.禁止这个泛型参数做方法的返回值:假如泛型参数Apple做方法的返回值,那么调用者可能就使用Apple类型去接收这个返回值,然而人家可能返回的只是一个Fruit,这肯定不行!所以泛型参数禁止做方法的返回值!

四、委托内的协变和逆变

上面说协变和逆变的时候只在接口内做了说明,这里说一下委托内的逆变和协变!
未完待续。。。

相关文章:

  • 2022-01-06
  • 2021-08-26
  • 2022-12-23
  • 2021-06-12
  • 2022-01-12
  • 2021-11-25
  • 2021-12-23
  • 2021-08-03
猜你喜欢
  • 2021-12-22
  • 2021-10-27
  • 2022-12-23
相关资源
相似解决方案