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

一、什么是协变和逆变

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

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

这个过程就称为协变

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

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

这个过程就成为逆变

我们先明确两个术语:

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

逆变(Contravariance):反转类型顺序。如果 Dog 是 Animal 的子类,那么 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>,它可以用于比较任何对象,包括 string 或 Person,所以可以赋值给 IComparer<string>

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

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

相关推荐
泛凡(Linyongui)13 小时前
基于 CW32L010 的智能厨卫调温旋钮方案--低成本 EC21 编码旋钮全解析
单片机·嵌入式硬件·c#
暴走约伯14 小时前
【VFX工具链源码拆解 02】告别机械劳动(上):一键统改百个子粒子颜色与批量重命名
3d·unity·c#·材质
信息安全失业大专人员16 小时前
每日了解一个智能体-WorkBuddy
开发语言·ai·c#·智能体
呆呆敲代码的小Y16 小时前
【实战精选面试题】C# 面试题详细解答,按难易程度排序
android·java·游戏·c#·游戏开发·unity面试题·c#面试题
淡海水17 小时前
09-01-内存-Unity资源生命周期全景
unity·c#·游戏引擎·.netcore·gc
椒颜皮皮虾྅20 小时前
让 C# 读懂整张图:DeploySharp 驱动 PaddleOCR v4/v5/v6,打通 OCR 全流程
开发语言·人工智能·深度学习·学习·c#·ocr·cuda
昂好的day20 小时前
C# 同步异步与多线程:Thread / Task / 线程池一次讲透(上篇)
开发语言·windows·c#
吃一碗粉只付一碗钱1 天前
12-给SPA友好的401和403响应
c#·状态模式
caimouse1 天前
MonoDevelop 编译用户应用程序时调用的是哪个编译器
c#
will_net2 天前
apollo 站点部署&C#接入
开发语言·c#·apollo