1 2 3 4 5 6 7 8 9 10

【C#开发】C#的协变和逆变

回想当时第一次学到协变和逆变的时候印象甚至都不是很深,也不是很能理解这个的作用和意义。
结果最近刷leetcode题的时候就遇到关于这个问题的情况。
最后解决了这个问题,以下是我解决问题的历程。


问题的出现

就比如力扣第15题三数之和

image

他的返回值是IList<IList< int >> 代表意思类似二维数组。


我第一反应就是先声明一个List<List< int >> res 的变量来作为答案,
并理所应当的认为泛型参数是支持隐式转换的。


但并不是这样滴
image



你得写成 List<IList< int >> 才行
image



这时我不禁心生疑问,难度他真的不支持隐式转换吗?
于是开始思考,上网查找资料,自己研究并总结


假设可以

我们首先思考如果接口泛型参数真的支持隐式转换会发生什么?



首先设DogAnimal类的子类

如果接口泛型参数真的支持隐式转换
那么35行不会报错,能正常执行
image
因为抽象接口泛型传入的是Animal类,
所以我们会发现36行In方法是可以传Animal类的,但是我们的实现类的泛型是Dog类,
这就意味着具体实现类的In方法是把这个泛型T看做Dog来处理的。

分析一下,这是因为抽象接口的In方法仅要求一个Animal类即可,
但是我们的具体实现类的In方法却是要Dog类。



假如具体实现类的In方法会说话,他可能会说:
"我要一个Dog类,你却传一个Animal类给我?"
这时的In方法相当于要你子类装父类,明显是不合理,是具有类型安全风险的。



但是仔细观察的话,Out方法却是合理的
image



因为接口的Out方法输出的是父类Animal
尽管实现类Out方法输出的是Dog
但是这相当于父类装子类:Animal res = new Dog(); //是合理的


豁然开朗

所以这时候协变的作用就出来了
在泛型参数T前加入out 关键字
image
35行的报错就解决了



使用协变(out)修饰T之后,T只能作为接口方法的返回值
所以把先前写的In方法注释了



如果理解了上面的协变的例子,那么逆变就好理解了。



我们将上面例子的泛型参数调过来,
抽象接口泛型用子类Dog,具体实现类泛型用父类Animal
image
跟上面协变的例子同理
但这次是In方法没问题,Out方法出现了问题。



分析一下,我们发现抽象接口的In方法要求传入一个Dog类,
但是我们的具体实现类的In方法仅仅要求Animal类。
我要的是父类,你给我子类,这很合理,父类装子类



但是Out方法就有疑问了
image
分析一下,我们发现抽象接口的Out方法会输出一个Dog类,
但是我们的具体实现类的Out方法仅会输出Animal类。
我要输出的是子类,你却给我父类,这很合理吗?




此时根据多态的原理,entity可能会说:
怎么编译之前我Out方法输出的是一个Dog子类
但是编译完之后我Out方法输出的就是更抽象的一个Animal父类了?




这并意味着这两个不能一起用,仅仅只是不能两个同时修饰同一个泛型参数T而已
可以一个用out(协变)来修饰T1,另一个用in(逆变)来修饰T2
image

回到开头

所以为什么IList<IList< int >> 不能装入List<List< int >>呢?
这时我们去翻看IList源码能发现,原来他并没有使用协变
image
因为他的泛型T既要作为方法的参数输入和输出
所以才用不了协变,IList<IList< int >> 才不装不了List<List< int >>



但值得注意的是IEnumerable是支持协变的
image



所以IEnumerable<IEnumerable< int >> 是能装List<List< int >>
image

总结

什么是协变和逆变?
协变 允许你在泛型中使用比指定类型更具体的类型。
例如IEntity < Animal > entity = new Entity< Dog >();
它适用于返回值类型,使用 out 关键字标记泛型类型参数。


逆变 允许你在泛型中使用比指定类型更抽象的类型。
例如IEntity < Dog > entity = new Entity< Animal >();
它适用于参数类型,使用 in 关键字标记泛型类型参数。


为什么需要协变和逆变?
在处理复杂的类型层次结构和泛型集合时,协变和逆变确保类型安全并提高代码的灵活性。
它能够允许我们在不牺牲类型安全的情况下实现"属于泛型的多态性"。

posted @ 2024-07-19 23:17  mayoyi  阅读(4)  评论(0编辑  收藏  举报