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

空传播是一个非常好的function – 但实际的魔法在哪里以及如何发生? frm?.Close()在哪里frm?.Close()变为if(frm != null) frm.Close(); – 它实际上是否真的改变了那种代码?

它由编译器完成。 它不会改变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

如果结果为false(即对象为null
poppop堆栈(清除堆栈,我们不再需要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 

注意上面的内容与?. 一样?. 但是检查的有效结果是一样的。

没有空检查:

 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 

它实际上是否真的改变了那种代码?

嗯,是的,但是在IL级别,而不是C#级别。 编译器发出的IL代码大致转换为您提到的等效C#代码。