泛型的协变(Covariance)和逆变(Contravariance)

一、什么是协变和逆变

如果存在一个泛型接口Interface<T>

它的泛型参数的子类型IInterface<Dog>可以安全的转换成泛型父类型的IInterface<Animal>

这个过程就称为协变

如果存在一个泛型接口IInterface<T>

它的泛型参数的父类型IInterface<Animal>可以安全的转换成泛型子类型的IInterface<Dog>

这个过程就成为逆变

我们先明确两个术语:

协变(Covariance):保持类型顺序。如果 DogAnimal 的子类,那么 IEnumerable<Dog> 可以被视为 IEnumerable<Animal>

逆变(Contravariance):反转类型顺序。如果 DogAnimal 的子类,那么 Action<Animal> 可以被视为 Action<Dog>

协变与逆变解决的问题

假设你有如下类层次:

csharp 复制代码
class Animal { }
class Dog : Animal { }

❌ 没有协变/逆变时的问题

在没有协变的情况下,以下代码会编译失败:

csharp 复制代码
List<Dog> dogs = new List<Dog>();

// 编译错误!List<Dog> 不能赋值给 List<Animal>
List<Animal> animals = dogs;

因为泛型默认是 不变(Invariant) 的,即 List<Dog>List<Animal> 没有继承关系。

但直觉上,狗的列表"应该是"动物列表的一种。协变就是为了解决这种合理的类型转换需求,同时保证类型安全。

实际应用场景

协变: 你有一个 IReadOnlyList<IDocument>,但实际传入的是 IReadOnlyList<PdfDocument>,协变让它可以接受。
逆变: 你有一个排序器IComparer<object>,它可以用于比较任何对象,包括 stringPerson,所以可以赋值给 IComparer<string>

C# 使用 out 和 in 关键字来声明协变和逆变:

out T:表示 协变(Covariance),T 只能作为方法的返回值(输出)。
in T :表示 逆变(Contravariance),T 只能作为方法的参数(输入)。

相关推荐
Ray Liang10 小时前
用六边形架构与整洁架构对比是伪命题?
java·python·c#·架构设计
Scout-leaf3 天前
WPF新手村教程(三)—— 路由事件
c#·wpf
用户298698530144 天前
程序员效率工具:Spire.Doc如何助你一键搞定Word表格排版
后端·c#·.net
mudtools5 天前
搭建一套.net下能落地的飞书考勤系统
后端·c#·.net
玩泥巴的5 天前
搭建一套.net下能落地的飞书考勤系统
c#·.net·二次开发·飞书
唐宋元明清21885 天前
.NET 本地Db数据库-技术方案选型
windows·c#
lindexi5 天前
dotnet DirectX 通过可等待交换链降低输入渲染延迟
c#·directx·d2d·direct2d·vortice
qq_454245035 天前
基于组件与行为的树状节点系统
数据结构·c#
bugcome_com5 天前
C# 类的基础与进阶概念详解
c#
雪人不是菜鸡5 天前
简单工厂模式
开发语言·算法·c#