You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#逆变中引用赋值兼容性反转的含义及方法组逆变调用疑问

解析C#逆变中的“引用赋值兼容性反转”及相关疑问

一、什么是“引用的赋值兼容性被反转”?

首先得先搞清楚正常的赋值兼容性:在C#里,派生类型的实例可以直接赋值给基类型的变量,比如:

string str = "test";
object obj = str; // 没问题,string是object的派生类

这是顺向的——从“更具体的类型”到“更抽象的类型”的赋值,编译器默认允许。

协变是保留这种顺向兼容性,比如IEnumerable<string>可以赋值给IEnumerable<object>,因为IEnumerable<T>是输出型接口(只返回T),返回的string能隐式转成object,所以这个赋值是安全的,和正常的赋值方向一致。

而逆变则是把这个方向反过来了,咱们看你给出的Action委托例子:

// 定义一个接受object参数的方法
static void SetObject(object o) { /* 处理object */ }

Action<object> actObject = SetObject;
// 关键:把接受object的委托赋值给接受string的委托变量
Action<string> actString = actObject;

按正常的赋值逻辑,Action<string>是“只能接受string参数的委托”,Action<object>是“能接受任何object的委托”——正常来说,你不能把“更宽泛的委托”赋值给“更具体的委托”,但逆变允许这么做,这就叫赋值兼容性被反转:原本基类型泛型参数的委托(Action<object>),可以赋值给派生类型泛型参数的委托(Action<string>),和普通引用赋值的方向完全相反。

二、为什么逆变的方法调用不会抛出InvalidCastException?

你担心的是“从低派生类型转高派生类型会抛异常”,但这里的情况根本不需要强制转换,反而完全安全:

当你调用actString("test")时,本质是把string类型的参数传给SetObject方法——而string本身就是object的派生类,它可以隐式转换为object,完全符合SetObject的参数要求。编译器之所以允许这种逆变赋值,就是因为它能保证调用的安全性:接受基类型参数的方法,必然能处理派生类型的参数(因为派生类型是基类型的子集,隐式转换合法)。

举个通俗的例子:你有一个能装“所有水果”的篮子(Action<object>),现在把它当成“只能装苹果”的篮子(Action<string>)用——往里面放苹果(string)当然没问题,因为苹果本来就是水果(object),根本不需要强制转换,自然不会抛出异常。

内容的提问来源于stack exchange,提问作者samus

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 09:05:04