每日一题:请解释 .NET 中的协变和逆变?

请解释 .NET 中的"协变(Covariance)"和"逆变(Contravariance)"是什么?它们在泛型中的作用是什么?

参考答案:

在 .NET 中,协变(Covariance) 和 逆变(Contravariance) 是泛型类型参数的一种类型转换机制,用来提高代码的灵活性。它们主要用于 接口和委托的泛型类型参数。

协变(Covariance) 指的是:

如果类型 A 是 B 的子类型,那么 Generic<A> 可以被当作 Generic<B> 使用。

在 C# 中,协变通过关键字 out 表示,通常用于 只返回数据而不接收数据的类型参数。

例如:

如果 Cat 继承 Animal,那么 IEnumerable<Cat> 可以被当作 IEnumerable<Animal> 使用,因为集合只是读取数据,而不会写入数据。

逆变(Contravariance) 则是相反的方向:

如果 A 是 B 的子类型,那么 Generic<B> 可以被当作 Generic<A> 使用。

在 C# 中,逆变通过关键字 in 表示,通常用于 只接收参数而不返回数据的类型参数。

例如,在委托参数中,如果某个方法接受 Animal 参数,它也可以处理 Cat 参数。

协变和逆变的设计主要是为了 提高泛型类型的兼容性和可复用性,减少类型转换带来的限制。

追问 1

为什么协变只能用于返回值,而不能用于参数?

答案:

协变的核心思想是允许返回更具体的类型。如果允许在参数位置使用协变,就可能导致类型安全问题。例如,如果一个接口声明可以接收 Animal,但实际传入的是 Cat 的集合,那么调用方可能向集合中添加 Dog,这就会破坏原本 Cat 集合的类型安全。

因此,C# 语言设计中规定协变类型参数只能用于 输出位置(返回值),而不能用于输入位置(方法参数)。这样可以保证泛型类型在转换时仍然保持类型安全,同时又能提供一定程度的灵活性。

追问 2

在实际开发中,哪些 .NET 类型使用了协变或逆变?

答案:

在 .NET 标准库中,很多常用接口都使用了协变或逆变来提高灵活性。例如 IEnumerable<out T> 使用协变,因为集合通常只用于读取数据;而 IComparer<in T> 和 IEqualityComparer<in T> 使用逆变,因为它们主要用于接收参数进行比较。

此外,许多委托类型(例如 Func<> 和 Action<>)也使用了协变和逆变。例如 Func<out TResult> 的返回值支持协变,而 Action<in T> 的参数支持逆变。

这些设计使得 .NET 的泛型 API 更加灵活,减少了类型转换代码,同时保持类型安全。

#面试题 #dotnet面试题 #面试真题

相关推荐
那咋乎吧41 分钟前
线程上下文切换和用户态进入内核态 linux内核都发生了什么
后端
YIAN1 小时前
NestJS 入门核心梳理:模块化架构、装饰器与依赖注入
后端·nestjs
唐青枫1 小时前
别只会 malloc:Zig Allocator、所有权与内存生命周期实战
后端
风曳丷1 小时前
12|用 Attack Tree 和 Trace 固化证据
后端
江华森2 小时前
云原生从0到1:Kubernetes 工作负载实战——Deployment/Service/滚动更新/弹性伸缩
前端·后端
江华森2 小时前
《云原生从0到1:4台华为云ECS搭建Kubernetes 1.28集群实录(上)——环境与踩坑全记录》
前端·后端
那咋乎吧2 小时前
数据包到达网卡后到用户态应用程序的全流程
后端
程序猿阿越2 小时前
kubelet源码阅读
后端·kubernetes·源码阅读
烂蜻蜓3 小时前
Flask入门教程(九):模板渲染——用Jinja2构建动态页面
后端·python·flask
QQ_21696290963 小时前
【源码编号:project93375】SpringBoot汽车维修管理信息系统:客户车辆、维修预约、工单派发、配件结算全流程实战
java·spring boot·后端·汽车·springboot·需求分析