如果为null,则实例化变量

时间:2011-02-06 19:56:28

标签: c# .net

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

x = x ?? new X();

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

4 个答案:

答案 0 :(得分:30)

查看中间语言代码会有所不同:

.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();
}

为了确保哪个更快,你应该两个时间。

Method     Initial condition   Iterations per second
---------------------------------------------------
NullCheck  x is null           33 million  
Coalesce   x is null           33 million
NullCheck  x is not null       40 million
Coalesce   x is not null       33 million

结论:

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

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

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

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

stfld

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

答案 1 :(得分:16)

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

答案 2 :(得分:6)

没有 interresting 性能差异 。最重要的是if (x == null) x = new X(); 更具可读性

回答您的原始问题:智能优化编译器将以与x = x ?? new X()相同的方式编译if (x == null) x = new X();

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


更新:要详细了解优化做法,请阅读this article on wikipedia,然后就问题的性质,Google “premature optimization is the root of all evil”

答案 3 :(得分:4)

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

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.