协变(Covariance)和逆变(Contravariance)
C#里的协变(Covariance)和逆变(Contravariance)主要是解决泛型类型之间能不能“安全替换”的问题,本质是“类型兼容性在泛型里的延伸”。
协变(out):👉 “可以用更具体的类型替换更通用的类型”
逆变(in):👉 “可以用更通用的类型替换更具体的类型”
1 | class Animal {} |
1.协变(out):需要Animal但给Dog也可以
特点:只能用于返回值,不能用于参数输入
示例:
1 | interface IProducer<out T> |
使用:
1 | IProducer<Dog> dogProducer = ...; |
因为Dog是Animal的子类
❗为什么安全?
你只是“取出来”,不会往里面塞东西。
反例:
1 | interface IBad<out T> |
2.逆变(in):需要Dog但给一个能处理的Animal也可以
特点:只能用于方法传参,不能用于返回值
示例:
1 | interface IConsumer<in T> |
使用
1 | IConsumer<Animal> animalConsumer = ...; |
❗为什么安全?
因为:
1 | dogConsumer.Consume(new Dog()); |
👉 实际调用的是:
1 | animalConsumer.Consume(Animal); |
✔️ Dog 也是 Animal → 安全
🚫 错误用法
1 | interface IBad<in T> |
| 类型 | 方向 | 记忆 |
|---|---|---|
| 协变 | 子 → 父 | “给我更具体也行” |
| 逆变 | 父 → 子 | “我能处理更泛的” |
何时用?
你在设计接口时:
✅ 用协变(out)
只返回 T 不接收 T
👉 典型:读取数据
✅ 用逆变(in)
只接收 T 不返回 T
👉 典型:处理/消费数据
总结一句话
👉 协变和逆变的本质:
让泛型在“只读”或“只写”的情况下变得更灵活且类型安全
完整例子
1 | using System; |
输出:
1 | === 协变(Covariance)示例 === |