编译器在用于自我分配时是否优化Null Coalescing运算符?

时间:2011-06-23 00:25:32

标签: .net c#-4.0

这两个代码块在功能上是相同的

if (myObj == null)
{
    myObj = new MyObj();
}

myObj = myObj ?? new MyObj();

但是,使用null合并运算符的那个在myObj不为null的情况下执行不必要的赋值。但后来我想也许编译器可以优化这些自我分配。有没有人知道编译器是否会注意到发生了什么,并且基本上将底部片段转换为顶部片段?

2 个答案:

答案 0 :(得分:5)

为了进行比较,我尝试编译两个

object myObj = null;
myObj = myObj ?? new object();

object myObj = null;
if(myObject == null)
{
  myObj = new object();
}

Main方法内部。 (我在Mono 2.6.7上使用MonoDevelop 2.4)

如果代码按预期进行了优化,我们应该会看到生成类似的IL。以下是第一版Main的IL:

.method public static  hidebysig 
       default void Main (string[] args)  cil managed 
{
    .entrypoint
    .maxstack 3
    .locals init (
            object  V_0)
    IL_0000:  ldnull 
    IL_0001:  stloc.0 
    IL_0002:  ldloc.0 
    IL_0003:  dup 
    IL_0004:  brtrue IL_000f

    IL_0009:  pop 
    IL_000a:  newobj instance void object::'.ctor'()
    IL_000f:  stloc.0 
    IL_0010:  ret 
}

和第二个版本:

.method public static  hidebysig 
       default void Main (string[] args)  cil managed 
{
    .entrypoint
    .maxstack 1
    .locals init (
            object  V_0)
    IL_0000:  ldnull 
    IL_0001:  stloc.0 
    IL_0002:  ldloc.0 
    IL_0003:  brtrue IL_000e

    IL_0008:  newobj instance void object::'.ctor'()
    IL_000d:  stloc.0 
    IL_000e:  ret 
}

所以第一个版本(使用null-coalescing运算符)产生了更多的IL。

但有两点需要注意:

  1. 这个IL是使用MoveDevelop得到的 - 如果你在Visual Studio中编译它,它很可能是一个不同的故事。也许它在微软的编译器中进行了优化。即使它们与它们相同,另一个人的编译器也可以轻松优化它。仅仅因为某些编译器存在某些东西,并不意味着你会期望它来自每个人的编译器。
  2. CLR使用JIT。即使在编译时没有对其进行优化,它也可能在运行时进行了优化。事实上,这可能就是为什么编译不是那么关注这种微优化的原因。

答案 1 :(得分:3)

刚刚在LinqPad中尝试过:

void Main()
{
    MyObj myObj = null;
    myObj = myObj ?? new MyObj();
}

这给出了以下IL:

IL_0000:  ldnull      
IL_0001:  stloc.0     
IL_0002:  ldloc.0     
IL_0003:  dup         
IL_0004:  brtrue.s    IL_000C
IL_0006:  pop         
IL_0007:  newobj      UserQuery+MyObj..ctor
IL_000C:  stloc.0     

所以似乎无论stloc.0是否为null,都会完成赋值(myObj在IL_000C)...但是JIT可能会稍后优化它。