C#空传播 - 魔法在哪里发生?

Dav*_*don 6 c# null propagation

空传播是一个非常好的功能 - 但实际魔法在哪里以及如何发生?哪里frm?.Close()得到改变if(frm != null) frm.Close();-它是否真正得到改变那种代码呢?

Rob*_*Rob 20

它由编译器完成.它不改变frm?.Close()if(frm != null) frm.Close();在重新编写源代码方面,但它发射IL字节码检查空.

请看以下示例:

void Main()
{
    Person p = GetPerson();
    p?.DoIt();
}
Run Code Online (Sandbox Code Playgroud)

编译为:

IL_0000:  ldarg.0     
IL_0001:  call        UserQuery.GetPerson
IL_0006:  dup         
IL_0007:  brtrue.s    IL_000B
IL_0009:  pop         
IL_000A:  ret         
IL_000B:  call        UserQuery+Person.DoIt
IL_0010:  ret         
Run Code Online (Sandbox Code Playgroud)

这可以理解为:

call- 调用GetPerson()- 将结果存储在堆栈中.
dup- 将值推入调用堆栈(再次)
brtrue.s- 弹出堆栈的最高值.如果为true,或者为null(引用类型),则转到IL_000B

如果结果为false(即对象为null)
pop- 弹出堆栈(清除堆栈,我们不再需要值Person)
ret- 返回

如果值为true(即,对象不为null)
call- 调用DoIt()堆栈的最顶层值(当前是结果GetPerson).
ret - 退货

手动空检查:

Person p = GetPerson();
if (p != null)
    p.DoIt();

IL_0000:  ldarg.0     
IL_0001:  call        UserQuery.GetPerson
IL_0006:  stloc.0     // p
IL_0007:  ldloc.0     // p
IL_0008:  brfalse.s   IL_0010
IL_000A:  ldloc.0     // p
IL_000B:  callvirt    UserQuery+Person.DoIt
IL_0010:  ret         
Run Code Online (Sandbox Code Playgroud)

请注意,上述内容并不相同?.,但检查的有效结果是相同的.

没有空检查:

void Main()
{
    Person p = GetPerson();
    p.DoIt();
}

IL_0000:  ldarg.0     
IL_0001:  call        UserQuery.GetPerson
IL_0006:  callvirt    UserQuery+Person.DoIt
IL_000B:  ret         
Run Code Online (Sandbox Code Playgroud)

  • 很好的答案,但如果你还显示`if(p!= null)`版本和IL会很好. (2认同)