C# 空传播 - 魔术发生在哪里
本文关键字:在哪里 魔术 传播 | 更新日期: 2023-09-27 18:31:38
空传播是一个非常好的功能 - 但是实际的魔术在哪里以及如何发生?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 或非 null(引用类型),则分支到 IL_000B
如果结果为假(即对象为空)
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
请注意,上述内容与?.
不同,但检查的有效结果是相同的。
无空检查:
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# 代码。