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

匿名类型为何未重载==和!=运算符(而记录类型却重载了)?

匿名类型为何未重载==和!=运算符(而记录类型却重载了)?

咱们先把现状掰扯清楚:匿名类型的Equals和GetHashCode方法是被重写过的——两个同类型的匿名实例,只有当所有属性值都相等时,才会被判定为相等,也就是值相等而非默认的引用相等。但奇怪的是,==和!=运算符却没被重载,依然保持着默认的引用相等判断逻辑。

其实我原本以为,编译器在处理匿名类型时,会自动生成类似这样的运算符重载代码:

public static bool operator ==(AnonymousType0 x, AnonymousType0 y) => x is not null && x.Equals(y);
public static bool operator !=(AnonymousType0 x, AnonymousType0 y) => !(x == y);

但后来出的C#记录类型就不一样了——它不仅重写了Equals方法,还同步重载了==和!=运算符,让两者的语义完全保持一致。

说起来,匿名类型和记录类型的设计目标其实非常相似:都是不可变的引用类型数据容器,主打值语义。这就让人忍不住好奇,当初语言设计团队为什么决定不给匿名类型重载这两个运算符呢?

其实我现在对C#里==的行为越来越困惑了:它有时候用引用相等,有时候用值相等;有时候和Equals的结果一致,有时候又完全不同;甚至把对象强制转换类型后,它的返回结果都可能变。而且现在C#已经有Equals、ReferenceEquals,还有is运算符了,单独实现的==运算符感觉已经没有明确的定位,反而容易让人混淆,引发不必要的bug。我甚至觉得,要是==不能被重载,默认就先做空检查,然后调用左操作数的Equals方法传入右操作数,反而更清晰。

哦对了,我也想到可能存在一些罕见的场景:比如把==重载成基于Guid的自定义身份相等——既不是值相等也不是引用相等,但微软的官方文档里好像从来没说这是==的预期用途。

另外还有个相关的问题是问“为什么匿名类型的Equals实现会比较字段”,但我的问题和它不一样:那个问题关注的是匿名类型为什么要用值语义的Equals,而我关心的是为什么==和!=没同步改成值语义,反而和记录、string这类同样是引用类型但重载了运算符的类型不一致。

我翻了微软的设计指南,发现这些内容其实能给点线索:

默认情况下,==运算符通过判断两个引用是否指向同一个对象来测试引用相等。因此,引用类型不需要实现==运算符就能获得此功能。当类型是不可变的(即实例中包含的数据不能更改)时,重载==运算符以比较值相等而非引用相等可能很有用,因为作为不可变对象,只要它们具有相同的值,就可以视为相同。

这份指南是在包含匿名类型的C# 3.0发布一年后编写的。还有另一份关于相等运算符的设计指南明确要求:

✔️ 要确保Object.Equals和相等运算符具有完全相同的语义和相似的性能特征。

这份指南则是基于C# 3.0发布次年出版的书籍第二版制定的。

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.08 09:00:26