为何实现隐式转换的C# Record生成不同Linq表达式?能否调整?
带隐式转换的C# Record生成不符合预期Linq表达式的问题解析
问题背景
在开发Entity Framework自定义类型时发现:当用Record/Record Struct实现和Class/Struct完全相同的隐式转换逻辑时,生成的Linq表达式存在差异。使用Record实现时EF会抛出「表达式不支持」的错误,而Class/Struct实现则能正常运行,根源在于两者对应的.NET表达式结构不同。
表达式差异对比
- Record/Record Struct生成的不符合预期的表达式:
System.Collections.Generic.List`1[TestObject].Where(x => (Convert(x.Id, Record) == value(Program+<>c__DisplayClass0_0)._record))
其中Convert(x.Id, Record)是EF默认不支持的转换逻辑。
- Class/Struct生成的符合预期的表达式:
System.Collections.Generic.List`1[TestObject].Where(x => (x.Id == Convert(value(Program+<>c__DisplayClass0_0)._class, Int32)))
根本原因
这个差异源于C#编译器对Record类型的重载解析规则:
- Record默认会生成基于值相等的
==运算符重载,而Class/Struct若未手动实现==,会使用引用相等(Class)或值相等的默认逻辑(Struct)。编译器解析x.Id == _record这类表达式时,对Record的处理优先级与Class/Struct不同。 - 当同时存在
int → Record和Record → int的双向隐式转换时,编译器会优先将左侧的int转换为Record类型,再调用Record默认的==运算符比较;而对于Class/Struct,编译器会优先将右侧的自定义类型转换为int后再做值比较。 - 注释掉Record其中一个方向的隐式转换(比如去掉
int → Record)时,编译器会自动选择将右侧的Record转换为int,从而生成符合预期的表达式。
让Record生成类似Class/Struct表达式的方法
可以通过以下方式调整:
- 保留单向隐式转换:仅实现
Record → int的隐式转换,移除int → Record的逻辑,编译器会自动将右侧Record转换为int进行比较。 - 显式重载Record的
==运算符:手动实现Record的==和!=运算符,明确指定与int比较时,先将Record转换为int再做值对比,覆盖默认相等逻辑。 - Linq表达式中显式转换:代码中手动将Record转为
int,比如x.Id == (int)_record,强制编译器生成预期的转换方向。
内容的提问来源于stack exchange,提问作者Vyrotek
相关产品推荐
相关产品推荐

