Null-Condition 运算符是否在连续使用站点中进行了优化,还是会导致重复检查

本文关键字:优化 检查 连续 是否 运算符 站点 Null-Condition | 更新日期: 2023-09-27 18:31:38

使用 null 条件运算符会重复 null 检查吗? 例如

var x = instance?.Property1;
var y = instance?.Property2;

这是否被编译成这样:

if (instance != null)
{
  var x = instance.Property1;
  var y = instance.Property2;
}

还是这个?

if (instance != null)
{
  var x = instance.Property1;
}
if (instance != null)
{
  var y = instance.Property2;
}

如果是前者,如果两行之间有其他代码,会有什么区别吗? 换句话说,编译器/优化器有多聪明?

Null-Condition 运算符是否在连续使用站点中进行了优化,还是会导致重复检查

编译器似乎对此一无所知。

代码:

var x = instance?.Property1;
var y = instance?.Property2;

。编译为非优化:

IL_0000:  nop         
IL_0001:  newobj      UserQuery+Class..ctor
IL_0006:  stloc.0     // instance
IL_0007:  ldloc.0     // instance
IL_0008:  brtrue.s    IL_000D
IL_000A:  ldnull      
IL_000B:  br.s        IL_0013
IL_000D:  ldloc.0     // instance
IL_000E:  ldfld       UserQuery+Class.Property1
IL_0013:  stloc.1     // x
IL_0014:  ldloc.0     // instance
IL_0015:  brtrue.s    IL_001A
IL_0017:  ldnull      
IL_0018:  br.s        IL_0020
IL_001A:  ldloc.0     // instance
IL_001B:  ldfld       UserQuery+Class.Property2
IL_0020:  stloc.2     // y
IL_0021:  ret         
Class..ctor:
IL_0000:  ldarg.0     
IL_0001:  call        System.Object..ctor
IL_0006:  nop         
IL_0007:  ret         

。并优化为:

IL_0000:  newobj      UserQuery+Class..ctor
IL_0005:  dup         
IL_0006:  dup         
IL_0007:  brtrue.s    IL_000C
IL_0009:  pop         
IL_000A:  br.s        IL_0012
IL_000C:  ldfld       UserQuery+Class.Property1
IL_0011:  pop         
IL_0012:  dup         
IL_0013:  brtrue.s    IL_0017
IL_0015:  pop         
IL_0016:  ret         
IL_0017:  ldfld       UserQuery+Class.Property2
IL_001C:  pop         
IL_001D:  ret         
Class..ctor:
IL_0000:  ldarg.0     
IL_0001:  call        System.Object..ctor
IL_0006:  ret         

两者都清楚地有两个分支检查。

它执行两个单独的 If.。否则,在您的情况下使用 null 条件运算符检查赋值。下面是一个反汇编的示例代码

源代码:

public class nulltest
{
    public void test()
    {
        var instance = new testclass();
        var x = instance?.prop1;
        var y = instance?.prop2;
    }
}
public class testclass
{
    public int prop1;
    public int prop2;
}

反汇编代码 (ILSpy):

public class nulltest
{
    public void test()
    {
        testclass testclass = new testclass();
        if (testclass == null)
        {
            int? arg_20_0 = null;
        }
        else
        {
            new int?(testclass.prop1);
        }
        if (testclass == null)
        {
            int? arg_3A_0 = null;
        }
        else
        {
            new int?(testclass.prop2);
        }
    }
}

我使用int作为属性的类型,但上述情况应该适用于任何其他情况。

编译器很严谨,想想这段代码。

class Tricky
{
    public int Property1
    {
        get
        {
            Program.instance = null;
            return 1;
        }
    }
    public int Property2
    {
        get
        {
            return 2;
        }
    }
}
class Program
{
    public static Tricky instance = new Tricky();
    public static void Main(string[] arg)
    {
        var x = instance?.Property1;
        var y = instance?.Property2;
        //what do you think the values of x,y
    }
}

预期结果为:x == 1, y is null 。但是,如果编译器使用一个 if 语句优化代码,则会抛出NullReferenceException。这意味着,使用一个if语句,不是一个聪明的优化,它不是一个优化,因为它是错误的。