理解 C# 泛型接口中的协变与逆变(抗变)
2022/4/5 14:19:26
本文主要是介绍理解 C# 泛型接口中的协变与逆变(抗变),对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
最近在看《C#高级编程(第九版)》这本书,看到了泛型接口这章。其中关于协变和逆变没太理解,讲得有点坑爹,网上查了许多资料,总算(感觉)弄清楚了,来这里记录一下。
一、协变和逆变是什么?
先从字面上理解 协变(Covariance)、逆变(Contravariance)。
co- 是英文中表示“协同”、“合作”的前缀。协变 的字面意思就是 “与变化的方向相同”。
contra- 是英文中表示“相反”的前缀,逆变 的字面意思就是是 “与变化方向相反”。
那么问题来了,挖掘机技术哪家强?这里的 变化方向 指的是什么?
C# 中对于对象(即对象引用),仅存在一种隐式类型转换,即 子类型的对象引用到父类型的对象引用的转换。这里的变化指的就是这种 子->父 的类型转换。
object o = "hello"; //string (子类)类型的引用转换为 object (父类)类型的引用
协变与逆变虽然从名字上看是两个完全相反的转换,但其实只是“子类型引用到父类型引用”这一过程在函数中使用的 两个不同阶段 而已,接下来将详细说明这点。
二、使用函数的不同阶段发生的类型转换
假设有一函数,接收 object
类型的参数,输出 string
类型的返回值:
string Method(object o) { return "abc"; }
那么在Main
函数中我们可以这样调用它:
string s = "abc"; object o = Method(s);
注意,这里发生了两次隐式类型转换:
-
在向函数输入时,参数
s
由string
类型转换为object
类型 -
在函数输出(返回)时,返回值 由
string
类型转换为object
类型
我们这里可以看作是函数签名可发生变换(不论函数的内容,不影响结果):
-
string Method(object o)
可变换为string Method(string o)
-
string Method(string o)
可变换为object Method(string o)
也就是说,在函数输入时,函数的 输入类型 可由 object
变换为 string
,父->子
在函数输出时,函数的 输出类型 可由string
变换为object
,子->父
三、理解泛型接口中的 in
、out
参数
没有指定in
、out
的情况
假设有一泛型接口,并且有一个类实现了此接口:
interface IDemo<T> { T Method(T value); } public class Demo : IDemo<string> { //实现接口 IDemo<string> public string Method(string value) { return value; } }
在Main
函数中这样写:
IDemo<string> demoStr = new Demo(); IDemo<object> demoObj = demoStr;
上面的这段代码中的第二行包含了一个假设:
IDemo<string>
类型能够隐式转换为 IDemo<object>
类型
这乍看上去就像“子类型引用转换为父类型引用” 一样,然而很遗憾,他们并不相同。假如可以进行隐式类型转换,那就意味着:
string Method(string value)
能转换为 object Method(object value)
从上一节中我们知道,在函数这输入和输出阶段,其类型可变化方向是不同的。
所以在C#中,要想应用泛型接口类型的隐式转换,需要讨论“输入”和“输出”两种情况。
接口仅用于输出的情况,协变
interface IDemo<out T> { //仅将类型 T 用于输出 T Method(object value); } public class Demo : IDemo<string> { //实现接口 public string Method (object value) { //别忘了类型转换! return value.ToString(); } }
在Main
函数中这样写:
IDemo<string> demoStr = new Demo(); IDemo<object> demoObj = demoStr;
可将 string Method (object value)
转换为 object Method (object value)
即可将 IDemo<string>
类型转换为 IDemo<object>
类型。
仅从泛型的类型上看,这是 “子->父” 的转换,与第一节中提到的转换方向相同,称之为“协变”。
接口仅用于输入的情况,逆变
同理我们可以给 T
加上 in
参数:
interface IDemo<in T> { //仅将类型 T 用于输入 string Method(T value); } public class Demo : IDemo<object> { //实现接口 public string Method (object value) { return value.ToString(); } }
在Main
函数中这样写:
IDemo<object> demoObj = new Demo(); IDemo<string> demoStr = demoObj;
这里可将 string Method (object value)
转换为 string Method (string value)
即可将 IDemo<object>
类型转换为 IDemo<string>
类型。
仅从泛型的类型上看,这是 “父->子” 的转换,与第一节中提到的转换方向相反,称之为“逆变”,有时也译作“抗变”或“反变”。
四、总结
以上只讨论了协变与逆变在方法中的情况,其实在属性中情况也相类似,不再说明。
可能大家也发现了,所谓“协”与“逆”都是只是一种表象,其内在本质为同一过程。
“协变”与“逆变”中的“协”与“逆”表示泛型接口在将类型参数仅用于输入或输出的情况下,其类型参数的隐式转换所遵循的规律。
协变
当泛型接口类型仅用于输出(使用关键词 out),其类型参数隐式转换所遵循的规律与对象引用的类型转换规律相同,称之为“协变”
逆变
当泛型接口类型仅用于输入(使用关键词 in),其类型参数隐式转换所遵循的规律与对象引用的类型转换规律相反,称之为“逆变”、“抗变”或“反变”。
=。= 感觉强类型语言有点坑爹啊
本文转自 https://segmentfault.com/a/1190000007005115,如有侵权,请联系删除。
这篇关于理解 C# 泛型接口中的协变与逆变(抗变)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2022-03-01沐雪多租宝商城源码从.NetCore3.1升级到.Net6的步骤
- 2024-12-06使用Microsoft.Extensions.AI在.NET中生成嵌入向量
- 2024-11-18微软研究:RAG系统的四个层次提升理解与回答能力
- 2024-11-15C#中怎么从PEM格式的证书中提取公钥?-icode9专业技术文章分享
- 2024-11-14云架构设计——如何用diagrams.net绘制专业的AWS架构图?
- 2024-05-08首个适配Visual Studio平台的国产智能编程助手CodeGeeX正式上线!C#程序员必备效率神器!
- 2024-03-30C#设计模式之十六迭代器模式(Iterator Pattern)【行为型】
- 2024-03-29c# datetime tryparse
- 2024-02-21list find index c#
- 2024-01-24convert toint32 c#