如果为null,则实例化变量

if (x == null) x = new X(); 

 x = x ?? new X(); 

这两个中哪一个实际上更高效? 一旦编译完成它们就会有效地结束( x = x;是NO-OP)?

查看中间语言代码有以下区别:

 .method private hidebysig instance void Method1() cil managed { .maxstack 8 L_0000: ldarg.0 L_0001: ldfld class X Program::x L_0006: brtrue.s L_0013 L_0008: ldarg.0 L_0009: newobj instance void X::.ctor() L_000e: stfld class X Program::x L_0013: ret } .method private hidebysig instance void Method2() cil managed { .maxstack 8 L_0000: ldarg.0 L_0001: ldarg.0 L_0002: ldfld class X Program::x L_0007: dup L_0008: brtrue.s L_0010 L_000a: pop L_000b: newobj instance void X::.ctor() L_0010: stfld class X Program::x L_0015: ret } 

这是我为了得到这个而编译的代码:

 void Method1() { if (x == null) x = new X(); } void Method2() { x = x ?? new X(); } 

要确定哪个更快,你应该两个时间。

方法初始条件每秒迭代次数
 -------------------------------------------------- - 
 NullCheck x为3300万  
 Coalesce x为3300万
 NullCheck x不是4000万
 Coalesce x不是3300万

结论:

  • 在这种情况下,它们大致相同,其中值最初为null。
  • 当x已经不为空时,使用if语句的方法比空合并运算符快得多。

x不为null时的差异看起来可能是由于空合并运算符将x的值分配回x(IL中的stfld ),而当x不为null时,空检查会跳过stfld指令。

两者都是如此之快,你必须有一个非常紧密的循环来注意到差异。 如果您已使用数据分析代码,则应该只进行这些性能优化。 不同的情况,不同版本的.NET,不同的编译器等可能会产生不同的结果。

如果有人想知道我如何得到这些结果或重现它们,这里是我使用的代码:

 using System; class X { } class Program { private X x; private X xNull = null; private X xNotNull = new X(); private void Method1Null() { x = xNull; if (x == null) x = xNotNull; } private void Method2Null() { x = xNull; x = x ?? xNotNull; } private void Method1NotNull() { x = xNotNull; if (x == null) x = xNotNull; } private void Method2NotNull() { x = xNotNull; x = x ?? xNotNull; } private const int repetitions = 1000000000; private void Time(Action action) { DateTime start = DateTime.UtcNow; for (int i = 0; i < repetitions; ++i) { action(); } DateTime end = DateTime.UtcNow; Console.WriteLine(repetitions / (end - start).TotalSeconds); } private void Run() { Time(() => { Method1Null(); }); Time(() => { Method2Null(); }); Time(() => { Method1NotNull(); }); Time(() => { Method2NotNull(); }); Console.WriteLine("Finished"); Console.ReadLine(); } private static void Main() { new Program().Run(); } } 

免责声明:没有基准是完美的,这个bechmark 远非完美,主要是为了保持简单。 我已经运行了许多不同的测试,例如,使用不同顺序的方法,首先使用和不使用“预热”,在不同的时间长度等等。我每次都得到大致相同的结果。 我没有任何证据可以certificate这种或那种方式,所以任何有利于一种方法或另一种方法的偏见都是偶然的。

我不担心这种过早的优化。 我确信C#编译器的设计人员足够聪明,可以为你做这件事。

根本没有令人感兴趣的性能差异。 最重要的是if (x == null) x = new X(); 更具可读性

回答你原来的问题:一个聪明的优化编译器会编译x = x ?? new X() x = x ?? new X()的方式与if (x == null) x = new X();

因此,在编译器上留下微优化,并专注于代码可读性和清晰度。


更新:要了解有关优化实践的更多信息,请阅读维基百科上的这篇文章 ,然后就Google问题的性质而言,谷歌“过早优化是所有邪恶的根源” 。

正如其他人所提到的,两者之间的性能差异并没有那么不同,但两者之间存在重要差异,尽管从你的例子中并不那么明显。

if (x == null) x = new X(); 创建一个潜在的竞争条件,其中x可以由检查null和创建之间的另一个线程新建,从而丢失一个对象。

x = x ?? new X(); 首先创建变量的副本,因此没有潜在的竞争条件。

在引用对象的情况下,这更是一个问题。 例如:

 if (x != null) x.DoSomething(); // this can cause a null reference exception // if another thread nulls x between the check // and the call to DoSomething() (x = x ?? new X()).DoSomething() // this should be fine.