.NET对象创建,速度更快?

这两种对象创建方式之间有区别吗?

new MyClass() { Id = 1, Code = "Test" }; 

要么

 MyClass c = new MyClass(); c.Id = 1; c.Code = "Test"; 

什么更快? 我假设2之间没有区别。

第二个可能几乎肯定会更快,因为从逻辑上讲,涉及的任务较少。 在第一种情况下,代码实际上相当于:

 MyClass tmp = new MyClass() tmp.Id = 1; tmp.Code = "Test"; MyClass c = tmp; 

当您声明一个变量时,JIT编译器很可能会忽略它们 – 如果您使用对象初始化程序分配给现有变量,它将无法执行此操作。

编辑:我刚刚尝试使用和不使用优化进行编译,在这个“新变量”的情况下, 如果它正在优化,C#编译器会忽略这两个。 否则它没有(但JIT仍然可以)。 在“重新分配”的情况下,它可以产生可观察到的差异,因此我不希望进行相同的优化。 我没有检查过。

我会非常惊讶地看到它实际上产生了显着差异的情况,所以我选择了更易读的选项,IMO是第一个。

编辑:我认为大家可能对基准测试感兴趣,表明它有所作为。 这是一个故意可怕的代码,使隐藏的额外任务变慢 – 我已经创建了一个大的,可变的结构。 Urgh。 无论如何…

 using System; using System.Diagnostics; struct BigStruct { public int value; #pragma warning disable 0169 decimal a1, a2, a3, a4, a5, a6, a7, a8; decimal b1, b2, b3, b4, b5, b6, b7, b8; decimal c1, c2, c3, c4, c5, c6, c7, c8; decimal d1, d2, d3, d4, d5, d6, d7, d8; #pragma warning restore 0169 } class Test { const int Iterations = 10000000; static void Main() { Time(NewVariableObjectInitializer); Time(ExistingVariableObjectInitializer); Time(NewVariableDirectSetting); Time(ExistingVariableDirectSetting); } static void Time(Func action) { Stopwatch stopwatch = Stopwatch.StartNew(); action(); stopwatch.Stop(); Console.WriteLine("{0}: {1}ms", action.Method.Name, stopwatch.ElapsedMilliseconds); } static int NewVariableObjectInitializer() { int total = 0; for (int i = 0; i < Iterations; i++) { BigStruct b = new BigStruct { value = i }; total += b.value; } return total; } static int ExistingVariableObjectInitializer() { int total = 0; BigStruct b; for (int i = 0; i < Iterations; i++) { b = new BigStruct { value = i }; total += b.value; } return total; } static int NewVariableDirectSetting() { int total = 0; for (int i = 0; i < Iterations; i++) { BigStruct b = new BigStruct(); b.value = i; total += b.value; } return total; } static int ExistingVariableDirectSetting() { int total = 0; BigStruct b; for (int i = 0; i < Iterations; i++) { b = new BigStruct(); b.value = i; total += b.value; } return total; } } 

结果(使用/ o + / debug-):

 NewVariableObjectInitializer: 3328ms ExistingVariableObjectInitializer: 3300ms NewVariableDirectSetting: 1464ms ExistingVariableDirectSetting: 1491ms 

我有些惊讶于NewVariableObjectInitializer版本比直接设置版本慢......看起来C#编译器没有像它对引用类型那样优化这种情况。 我怀疑在值类型周围有一些微妙的东西阻止它。

我通过创建1亿个对象进行测试,每个对象使用参数化构造函数,带有初始化器的无参数构造函数和带有setter的无参数构造函数,并且根本没有可测量的差异。 执行时间略有不同,但以不同顺序运行测试会改变结果,因此差异只是由于垃圾收集器在不同时间启动。

创建1亿个对象大约需要1.5秒,因此没有太多理由尝试使其更快。

我个人更喜欢参数化构造函数,因为我可以使属性setter私有,以便我可以使类不可变,如果我想:

 class MyClass { public int Id { get; private set; } public string Code { get; private set; } public MyClass(int id, string code) { Id = id; Code = code; } } 

此外,通过这种方式,您可以确保在创建对象时已正确设置所有属性。

为了说明M Skeet的代码,这里是IL(注意方法#1的附加ldloc stloc)

  IL_0001: newobj instance void ConsoleApplication1.Program/MyClass::.ctor() IL_0006: stloc.2 IL_0007: ldloc.2 IL_0008: ldc.i4.1 IL_0009: callvirt instance void ConsoleApplication1.Program/MyClass::set_Id(int32) IL_000e: nop IL_000f: ldloc.2 IL_0010: ldstr "Test" IL_0015: callvirt instance void ConsoleApplication1.Program/MyClass::set_Code(string) IL_001a: nop IL_001b: ldloc.2 IL_001c: stloc.0 IL_001d: newobj instance void ConsoleApplication1.Program/MyClass::.ctor() IL_0022: stloc.1 IL_0023: ldloc.1 IL_0024: ldc.i4.1 IL_0025: callvirt instance void ConsoleApplication1.Program/MyClass::set_Id(int32) IL_002a: nop IL_002b: ldloc.1 IL_002c: ldstr "Test" IL_0031: callvirt instance void ConsoleApplication1.Program/MyClass::set_Code(string) IL_0036: nop 

他们是一样的。 但我们都更喜欢第一个,它更可读,更清晰,不是吗?