空传播是一个非常好的功能 - 但 和 实际的魔法是什么?
frm?.Close()
在哪里更改为if(frm != null) frm.Close();
- 它实际上是否真的更改为那种代码?
答案 0 :(得分:19)
由编译器完成。在重写源代码方面,它不会将frm?.Close()
更改为if(frm != null) frm.Close();
,但 会发出检查null的IL字节码。
采用以下示例:
void Main()
{
Person p = GetPerson();
p?.DoIt();
}
编译为:
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
可以理解为:
call
- 调用GetPerson()
- 将结果存储在堆栈中
dup
- 将值推送到调用堆栈(再次)
brtrue.s
- 弹出堆栈的最高值。如果为true或not-null(引用类型),则转移到IL_000B
如果结果为假(即对象 null ),则为
pop
- 弹出堆栈(清除堆栈,我们不再需要Person
的值)
ret
- 返回
如果值为true(即,对象 not 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
请注意,以上不与?.
相同,但检查的有效结果是相同的。
没有空检查:
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
答案 1 :(得分:2)
它实际上是否真的改变了那种代码?
嗯,是的,但是在IL级别,而不是C#级别。编译器发出的IL代码大致转换为您提到的等效C#代码。