0

0

c# 协变和逆变是什么

畫卷琴夢

畫卷琴夢

发布时间:2026-01-03 02:39:31

|

910人浏览过

|

来源于php中文网

原创

协变(out)和逆变(in)是c#编译期强制校验的类型安全机制:out用于只输出(返回值)的泛型参数,支持子类→父类转换;in用于只输入(参数)的泛型参数,支持父类→子类转换;二者不可共存,且仅适用于接口和委托。

c# 协变和逆变是什么

协变逆变是 C# 中让泛型接口和委托支持“安全类型转换”的机制,不是语法糖,也不是运行时魔法——它们由编译器在编译期强制校验,核心目标只有一个:在保持类型安全的前提下,让继承关系能自然地“传导”到泛型参数上

为什么 IEnumerable<string></string> 能直接赋值给 IEnumerable<object></object>

因为 IEnumerable<out t></out> 声明了 T 是协变的(用 out 修饰),意味着:只要 stringobject 的子类,那 IEnumerable<string></string> 就可隐式转为 IEnumerable<object></object>。这符合直觉——你从集合里“读出来”的东西,子类能当父类用(里氏替换原则)。

  • ✅ 合法:
    IEnumerable<string> strings = new List<string>();
    IEnumerable<object> objects = strings; // 协变生效
  • ❌ 非法:
    IList<string> strings = new List<string>();
    IList<object> objects = strings; // 编译错误!IList<T> 不是协变的
    (因为 IList<t></t> 既有 Get 又有 Set,无法同时满足协变/逆变约束)
  • ⚠️ 注意:数组也支持协变(如 string[]object[]),但它是**不安全的**——运行时可能抛 ArrayTypeMismatchException,而泛型协变是编译期就拦住的,更可靠。

为什么 Action<object></object> 能接收 Action<string></string>

因为 Action<in t></in> 声明了 T 是逆变的(用 in 修饰),意思是:你传进去的委托,参数类型越“宽”(越靠继承链顶端),它越能接受“窄”的实际参数。比如一个能处理任意 object 的方法,当然也能安全处理 string

Shell脚本编写基础 中文WORD版
Shell脚本编写基础 中文WORD版

Shell本身是一个用C语言编写的程序,它是用户使用Linux的桥梁。Shell既是一种命令语言,又是一种程序设计语言。作为命令语言,它交互式地解释和执行用户输入的命令;作为程序设计语言,它定义了各种变量和参数,并提供了许多在高级语言中才具有的控制结构,包括循环和分支。它虽然不是Linux系统核心的一部分,但它调用了系统核心的大部分功能来执行程序、建立文件并以并行的方式协调各个程序的运行。因此,对于用户来说,shell是最重要的实用程序,深入了解和熟练掌握shell的特性极其使用方法,是用好Linux系统

下载
  • ✅ 合法:
    Action<object> actObj = o => Console.WriteLine(o);
    Action<string> actStr = actObj; // 逆变生效:string 是 object 的子类
    actStr("hello"); // 安全调用,o 接收 string 没问题
  • ❌ 非法:
    Func<string> funcStr = () => "a";
    Func<object> funcObj = funcStr; // 编译错误!Func<T> 的 T 是 out,但 Func<string> 返回 string,不能当 Func<object> 用(返回值太具体)
    (等等——这里错了?不,Func<out t></out> 是协变的,所以 Func<string></string> ✅ 可赋给 Func<object></object>;真正非法的是反过来)
  • ? 关键记忆点:out = 输出(只读、返回值)、in = 输入(只写、参数);违反这个方向就会编译失败。

自己定义接口时,inout 怎么选?

不是看“类的继承方向”,而是看泛型参数 T 在接口方法中“出现的位置”:

  • 如果 T 只出现在返回值位置(如 T Get();),用 out T(协变);
  • 如果 T 只出现在方法参数位置(如 void Set(T value);),用 in T(逆变);
  • 如果 T 同时出现在返回值和参数中(如 T Convert(T input);),那就不能加 in/out——只能是不变(invariant),否则类型系统无法保证安全。
  • ⚠️ 常见坑:IComparer<t></t>in T,因为 Compare(T x, T y) 两个参数都是输入;IEqualityComparer<t></t> 同理;而 IComparable<t></t>in TCompareTo(T other) 参数是输入),别记反。

最易被忽略的一点:协变/逆变只适用于泛型接口和委托,不支持泛型类(如 List<t></t>)、不支持值类型(intDateTime 等不能参与 in/out)、也不支持泛型约束中的协变类型(比如 where T : out U 是非法的)。它是一套编译器强约束的“契约”,不是开发者自由发挥的空间。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
string转int
string转int

在编程中,我们经常会遇到需要将字符串(str)转换为整数(int)的情况。这可能是因为我们需要对字符串进行数值计算,或者需要将用户输入的字符串转换为整数进行处理。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

930

2023.08.02

string转int
string转int

在编程中,我们经常会遇到需要将字符串(str)转换为整数(int)的情况。这可能是因为我们需要对字符串进行数值计算,或者需要将用户输入的字符串转换为整数进行处理。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

930

2023.08.02

int占多少字节
int占多少字节

int占4个字节,意味着一个int变量可以存储范围在-2,147,483,648到2,147,483,647之间的整数值,在某些情况下也可能是2个字节或8个字节,int是一种常用的数据类型,用于表示整数,需要根据具体情况选择合适的数据类型,以确保程序的正确性和性能。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

601

2024.08.29

c++怎么把double转成int
c++怎么把double转成int

本专题整合了 c++ double相关教程,阅读专题下面的文章了解更多详细内容。

294

2025.08.29

C++中int的含义
C++中int的含义

本专题整合了C++中int相关内容,阅读专题下面的文章了解更多详细内容。

212

2025.08.29

javascriptvoid(o)怎么解决
javascriptvoid(o)怎么解决

javascriptvoid(o)的解决办法:1、检查语法错误;2、确保正确的执行环境;3、检查其他代码的冲突;4、使用事件委托;5、使用其他绑定方式;6、检查外部资源等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

186

2023.11.23

java中void的含义
java中void的含义

本专题整合了Java中void的相关内容,阅读专题下面的文章了解更多详细内容。

125

2025.11.27

硬盘接口类型介绍
硬盘接口类型介绍

硬盘接口类型有IDE、SATA、SCSI、Fibre Channel、USB、eSATA、mSATA、PCIe等等。详细介绍:1、IDE接口是一种并行接口,主要用于连接硬盘和光驱等设备,它主要有两种类型:ATA和ATAPI,IDE接口已经逐渐被SATA接口;2、SATA接口是一种串行接口,相较于IDE接口,它具有更高的传输速度、更低的功耗和更小的体积;3、SCSI接口等等。

1800

2023.10.19

PHP高性能API设计与Laravel服务架构实践
PHP高性能API设计与Laravel服务架构实践

本专题围绕 PHP 在现代 Web 后端开发中的高性能实践展开,重点讲解基于 Laravel 框架构建可扩展 API 服务的核心方法。内容涵盖路由与中间件机制、服务容器与依赖注入、接口版本管理、缓存策略设计以及队列异步处理方案。同时结合高并发场景,深入分析性能瓶颈定位与优化思路,帮助开发者构建稳定、高效、易维护的 PHP 后端服务体系。

4

2026.03.04

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
C# 教程
C# 教程

共94课时 | 10.7万人学习

C 教程
C 教程

共75课时 | 5.2万人学习

C++教程
C++教程

共115课时 | 20.5万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号