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 )
pop
– 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#代码。