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

C#表达式中new Object()与new Object{}的区别及初始化影响

空对象初始化器大括号在表达式树中的关键影响

这个问题其实是踩了「常规C#代码等价,但表达式树结构不同」的坑,我来给你拆解清楚:

一、常规代码 vs 表达式树的核心差异

在普通的C#执行代码里,new TDest()和new TDest{}确实是完全等价的——都会调用类的无参构造函数,没有任何功能差异,所以ReSharper的RedundantEmptyObjectOrCollectionInitializer规则在普通代码里是完全安全的。

但表达式树是用来描述代码结构的抽象语法树,它会精准保留你写的语法细节,这时候两种写法就产生了关键差异:

  • new TDest{}会被编译成 MemberInitExpression 类型的表达式节点:这个节点包含两部分——一个创建对象的NewExpression(调用无参构造),加上一个空的成员初始化列表。
  • new TDest()则直接编译成 NewExpression 类型的节点:仅仅表示调用无参构造函数,没有任何成员初始化的相关信息。

二、重构后代码失效的原因

你的代码大概率是依赖了表达式树的节点类型来做逻辑处理——比如某些对象映射库(像AutoMapper)、ORM框架(比如EF Core的查询表达式解析),或者你自己写的表达式树解析逻辑,会判断表达式节点是否是MemberInitExpression,然后执行对应的处理逻辑(哪怕初始化列表是空的)。当你改成new TDest()后,节点变成了NewExpression,原来的解析逻辑找不到预期的节点类型,就会抛出异常或者出现不符合预期的行为。

你可以用一段简单的代码验证这个差异:

// 定义测试类
public class TestSource {}
public class TestDest {}

// 原表达式
Expression<Func<TestSource, TestDest>> expr1 = model => new TestDest{};
Console.WriteLine(expr1.Body.GetType().Name); // 输出:MemberInitExpression

// 重构后的表达式
Expression<Func<TestSource, TestDest>> expr2 = model => new TestDest();
Console.WriteLine(expr2.Body.GetType().Name); // 输出:NewExpression

三、总结

ReSharper的这个重构规则没有考虑表达式树的特殊场景,它默认两种写法语义等价,但在表达式树的语境下,空对象初始化器{}和直接调用构造函数()的语法结构会被记录成不同的节点类型,导致依赖节点类型的代码逻辑失效。所以在表达式树中,这两种写法并不是可互换的。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 09:17:16