C# 泛型协变与逆变

一、基础:普通类的父子转换(铺垫)

1. 隐式转换(向上转型)

子类对象可以直接赋值给父类变量(隐式转换、安全)

cs 复制代码
Bird bird = new Bird();      
Sparrow sp = new Sparrow();

sp = new Sparrow();
bird = sp;   // 隐式转换:子类 → 父类(小范围 → 大范围,安全)

2. 显式强制转换(向下转型)

父类对象不能直接赋值给子类变量,必须强制转换

cs 复制代码
// sp = bird; // 直接报错:父类不能隐式转子类
sp = (Sparrow)bird; // 强制转换:父类 → 子类

总结

子类 → 父类:隐式转换(天然支持)

父类 → 子类:必须强制转换


二、核心难点:List<T> 为什么不支持转换?

代码报错原因(必考)

cs 复制代码
List<Bird> birds = new List<Bird>();
List<Sparrow> sparrows = new List<Sparrow>();

// birds = sparrows; 【编译报错】

原理

Bird 和 Sparrow 是父子关系,但是 List<Bird> 和 List<Sparrow> 没有任何继承关系!

List 是普通泛型类,不具备协变/逆变能力,所以无法互相赋值。


三、协变(out T)------ 子类泛型 → 父类泛型

1. 协变关键字:out

2. 协变规则(三条铁律)

  • 转换方向:子类泛型 → 父类泛型(正向、向上)

  • 只能用于:泛型接口 / 泛型委托

  • 语法:泛型参数前加 out T

3. 协变经典案例(IEnumerable<out T>)

cs 复制代码
// 支持协变:子类集合 → 父类集合
IEnumerable<Bird> birds1 = new List<Sparrow>();

原因:IEnumerable<out T> 接口 T 带 out 协变标识。

4. 委托协变案例(Func 返回值协变)

cs 复制代码
// Func<in T, out TResult> 返回值支持协变
Func<int, Sparrow> f1 = a => new Sparrow();
Func<int, Bird> f2 = f1; 
// 协变:Sparrow(子类) 返回值 → Bird(父类) 返回值

5. 常见支持协变的系统接口/委托

  • IEnumerable<out T>

  • IReadOnlyList<out T>

  • IReadOnlyCollection<out T>

  • Func<out TResult>


四、逆变(in T)------ 父类泛型 → 子类泛型

1. 逆变关键字:in

2. 逆变规则(三条铁律)

  • 转换方向:父类泛型 → 子类泛型(反向、向下)

  • 只能用于:泛型接口 / 泛型委托

  • 作用于:方法输入参数

3. 逆变全套案例

cs 复制代码
// 1. Predicate 委托逆变
Predicate<Bird> f3 = a => true;
Predicate<Sparrow> f4 = f3; 
// 逆变:父类泛型 → 子类泛型

// 2. Action 委托逆变
Action<object> f5 = a => { };
Action<Bird> f6 = f5;  

// 3. Func 参数逆变
Func<Bird, int> f7 = a => 10;
Func<Sparrow, int> f8 = f7; 

4. 常见支持逆变的系统接口/委托

  • Action<in T>

  • Predicate<in T>

  • IComparer<in T>

  • IEqualityComparer<in T>


五、既协变又逆变:Func<in T, out TResult>

Func 委托同时拥有两种特性:

  • 参数 in T:支持逆变(父参数 → 子参数)

  • 返回值 out TResult:支持协变(子返回值 → 父返回值)

万能双向转换委托!


六、最终总结对照表(必背)

类型 关键字 转换方向 适用位置
协变 out 子类泛型 → 父类泛型 返回值、集合读取
逆变 in 父类泛型 → 子类泛型 方法输入参数

七、完整可运行基类代码

cs 复制代码
public class Bird
{

}
public class Sparrow : Bird
{

}

八、口诀(秒杀面试题)

out协变,子转父;

in逆变,父转子;

List普通不转换,接口委托才支持;

参数in来返回out,双向Func最省事。

相关推荐
F202269748612 小时前
西门子 PLC 与 C# 通信
开发语言·c#
love530love16 小时前
用自然语言让 AI Agent 卸载软件 —— 以卸载 Visual Studio 2026 为例
ide·人工智能·windows·agent·visual studio·ai agent·marvis
Yang_jie_0316 小时前
笔记:数据结构(顺序表)
数据结构·windows·笔记
吴可可12317 小时前
C#实现CAD框选闭合图元外偏移1毫米
c#
꒰ঌ 安卓开发໒꒱17 小时前
.NET CAP入门到入土
后端·c#·.net
三十岁老牛再出发20 小时前
07.07.每日总结
c语言·windows·python
神经智研社20 小时前
ros2 -9章 机器人的控制多节点启动文件Launch
人工智能·microsoft·机器人环境搭建·win11 ros2 开发环境·ros2 jazzy
小鹿软件办公20 小时前
单文件狂吞 503 GB!微软确认 Windows 11 存储漏洞
microsoft·c盘存储漏洞·c盘占用太大如何清理
逝水无殇1 天前
C# 枚举(Enum)详解
开发语言·后端·c#