泛型的协变(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 只能作为方法的参数(输入)。

相关推荐
唐青枫6 小时前
别再手写 args[0]:C#.NET CommandLineParser 命令行工具实战详解
c#·.net
LccKyI7 小时前
C#学习day06(方法/函数及其关键字,附思维导图)
经验分享·学习·c#
.Peter7 小时前
.NET/WPF 程序在部分 Windows 电脑无法保存用户环境变量:使用 DPAPI 实现安全兼容
windows·信息安全·c#·.net·wpf·环境变量·dpapi
农村小镇哥8 小时前
怎么把HTM格式转化成WORD
ui·c#·word
农村小镇哥8 小时前
C#读取CSV文件的方法
服务器·数据库·c#
FuckPatience9 小时前
C# 报错参数无效
c#
北域码匠1 天前
PID 控制算法完整详解(C# 原生实现,无第三方库)
c#·pid控制·工控开发·闭环控制·自动控制算法·数字pid·增量式pid
离陌在学C#1 天前
C# 重载与重写:深入理解面向对象编程的核心概念
java·c#
asdzx671 天前
Java 实战:基于 Spire.Doc 高效提取 Word 文档文本与图片
java·c#·word
小金子会发光1 天前
C# WinForms 基于 Socket 手搓 Modbus TCP 调试助手(支持 01/02/03/04/05/06/0F/10)
c#·socket·plc·modbus tcp·工业通信