当前位置: 首页 > news >正文

C#中协变逆变的实现

1. 协变与逆变的概念

  • 协变(Covariance)
    允许将子类(派生类)类型作为父类(基类)类型使用。例如:IEnumerable<string> 可以被视为 IEnumerable<object>,因为 string 是 object 的子类。按照前面我们在继承中学习的里氏替换原则,父类装子类,是不是就非常的合理,因为一定会确保类型安全嘛。

  • 逆变(Contravariance)
    允许将父类类型作为子类类型使用。例如:Action<object> 可以被视为 Action<string>,因为 object 是 string 的父类。你看,居然父类可以变成子类,你想想,你的父亲有一天居然可以化形为你的儿子来使用,是不是就很反常识,不过这种转化肯定是有限制滴,不然啥都可以转化,是不是就整个面向对象就乱成了一锅粥。

核心思想:在类型安全的前提下,允许更灵活的泛型类型转换。

2. 协变与逆变的作用及作用对象

作用

  • 提 高泛型接口和委托的灵活性,使其能更自然地处理继承关系。

  • 减少强制 类型转换,增强代码的可读性和安全性。

作用对象

  • 泛型接口(如 IEnumerable<T>)
  • 泛型委托(如 Func<T>、Action<T>)
  • 不适用:类、结构体或方法参数(仅支持接口和委托)。

请务必记住协变逆变的作用对象,仅仅只能在泛型接口和泛型委托中使用,在其他的地方是万万不行滴,不然就会天下大乱啦!!!

3. 协变与逆变的关键字

out关键字(协变)
标记泛型类型参数为协变,表示该参数仅用于输出位置(如返回值)。啥意思?就是说T这个泛型类型,在被out修饰了以后,他就只能被当成返回值的类型了,不能当做函数中传参时候的类型了,为什么呢?因为你总要做些区分嘛,要先列好规矩,互相才能正常运行嘛,不然,你玩儿你的,我打我打的,整个继承和多态就乱成了一锅粥。

1

interfaceICovariant<outT> { T GetItem(); }

in关键字(逆变)
标记泛型类型参数为逆变,表示该参数仅用于输入位置(如方法参数)。啥意思?就是说T这个泛型类型,在被in修饰了以后,他就只能被当成函数传参时候的类型了,不能当做返回值类型。

1

interfaceIContravariant<inT> {voidProcess(T item); }

4. 泛型接口与委托的示例

示例1:协变在泛型接口中的体现

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

// 协变接口定义

interfaceIAnimal<outT>

{

T GetAnimal();

}

classAnimal { }

classDog : Animal { }

classAnimalShelter : IAnimal<Animal>

{

publicAnimal GetAnimal() =>newAnimal();

}

classDogShelter : IAnimal<Dog>

{

publicDog GetAnimal() =>newDog();

}

// 使用协变

IAnimal<Animal> shelter =newDogShelter();// 合法:DogShelter → AnimalShelter

Animal animal = shelter.GetAnimal();// 安全获取Animal类型

示例2:逆变在泛型接口中的体现

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

// 逆变接口定义

interfaceIFeeder<inT>

{

voidFeed(T animal);

}

classAnimal { }

classDog : Animal { }

classAnimalFeeder : IFeeder<Animal>

{

publicvoidFeed(Animal animal) => Console.WriteLine("Feeding animal");

}

classDogFeeder : IFeeder<Dog>

{

publicvoidFeed(Dog dog) => Console.WriteLine("Feeding dog");

}

// 使用逆变

IFeeder<Dog> feeder =newAnimalFeeder();// 合法:AnimalFeeder → DogFeeder

feeder.Feed(newDog());// 安全传递Dog类型给需要Animal的方法

//打印Feeding animal

示例3:协变在泛型委托中的体现

1

2

3

4

5

// 协变委托

delegateT Factory<outT>();

Factory<Dog> dogFactory = () =>newDog();

Factory<Animal> animalFactory = dogFactory;// 合法:Dog → Animal

Animal animal = animalFactory();

示例4:逆变在泛型委托中的体现

1

2

3

4

5

// 逆变委托

delegatevoidHandler<inT>(T obj);

Handler<Animal> animalHandler = (Animal a) => Console.WriteLine("Handle animal");

Handler<Dog> dogHandler = animalHandler;// 合法:Animal → Dog

dogHandler(newDog());// 安全调用

总结

特性关键字方向适用场景
协变out子类→父类返回值、集合遍历(如 IEnumerable<T>)
逆变in父类→子类方法参数、回调处理(如 Action<T>)

协变:out修饰的泛型替代符,只能作为返回值,不能作为参数
逆变:in修饰的泛型替代符,只能作为参数,不能作为返回值
协变:父类委托容器可以装 子类泛型委托容器
逆变:子类委托容器可以装 父类泛型委托容器

注意事项

  • 协变类型参数必须仅用于返回值位置。
  • 逆变类型参数必须仅用于方法参数位置。
  • 不支持类的协变/逆变,仅限接口和委托。
http://www.jsqmd.com/news/875734/

相关文章:

  • 别再折腾Linux了!用FreeSSHD+FileZilla在Windows上5分钟搞定SFTP服务器(附Nginx文件预览)
  • 基于柯西-施瓦茨不等式的数据融合与部分识别方法
  • 拓扑信号处理进阶:狄拉克方程与IDESP算法解析
  • 广义随机占优与偏序数据:处理混合尺度数据的鲁棒统计方法
  • 第一性原理与机器学习融合的高通量材料筛选:以无铅钙钛矿为例
  • C#实现ASCII和字符串相互转换的代码示例
  • 别再乱改系统时间了!Linux服务器时间漂移的终极排查与修复指南(hwclock实战)
  • 基于大数据与机器学习的金融风险监控系统架构与实战
  • 机器学习加速高精度CFD:基于分区POD与加权RBF的翼型流场快速预测
  • 量子高斯过程在电网参数辨识中的应用:NISQ时代的工程实践
  • Cortex-R82 AXI接口256位事务机制与优化
  • 语义网与知识图谱:从RDF三元组到LLM融合的技术演进与应用实战
  • SPACIER系统:贝叶斯优化与分子动力学融合的聚合物智能设计
  • 线性最优传输(LOT)在点云数据处理中的应用:从理论到实践
  • VMware里CentOS磁盘挂了别急着重装!记一次xfs文件系统修复实战,省下半天配置时间
  • 量子计算与生成式AI融合:自动化电路生成技术解析
  • 告别混乱:如何在不同Linux发行版(openEuler/Ubuntu)和Windows上彻底卸载AWS CLI v2
  • 几何量子机器学习:利用对称性原理破解贫瘠高原与设计高效算法
  • 天文机器学习项目实践指南:从问题定义到科学成果的可靠路径
  • 内存访问向量技术如何提升CPU性能模拟精度
  • 基于低秩分解与DLinear的流体动力学数据高效预测模型
  • 速腾RS-M1雷达点云初体验:Windows 11下用RSView 3.2.7从接线到显示的保姆级避坑指南
  • Wireshark解密HTTPS流量:TLS密钥导出与解密实战指南
  • Win10更新后网卡驱动感叹号?先别重置网络!检查这两个服务项(WLAN AutoConfig/蓝牙支持)
  • kNN×KDE算法:为缺失数据插补提供概率分布,提升天文数据分析可靠性
  • 芯片设计中Liberty模型555ns值的由来与应用
  • 可解释多模态机器学习在碳纳米管纤维性能优化与机理研究中的应用
  • IEMOCAP数据集预处理实战:用Python和Librosa搞定语音情感识别的数据准备
  • 2026年4月有名的光伏电站运维口碑推荐,光伏电站投资/储能电站安装/光伏电站运维/重卡充电桩安装,光伏电站运维推荐 - 品牌推荐师
  • IoT系统性能优化:PCA降维与智能负载均衡实战解析