C# Null 传播——魔法在哪里发生?
C# Null propagation - Where does the magic happen?
空值传播是一个非常好的功能 - 但是 where and how 真正的魔法真的发生了吗?
frm?.Close()
在哪里更改为 if(frm != null) frm.Close();
- 它实际上完全更改为那种代码吗?
这是编译器完成的。就 re-writing 源代码而言,它不会将 frm?.Close()
更改为 if(frm != null) frm.Close();
,但它 会 发出检查空值的 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
- 弹出堆栈的顶部值。如果为真,或者not-null(引用类型),则分支到IL_000B
如果结果为false(即对象为null)
pop
- 弹出堆栈(清除堆栈,我们不再需要 Person
的值)
ret
- Returns
如果值为真(即对象不为空)
call
- 在堆栈的 top-most 值上调用 DoIt()
(当前是 GetPerson
的结果)。
ret
- Returns
手动空检查:
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
Does it actually get changed to that kind of code at all?
嗯,是的,但在 IL 级别,而不是 C# 级别。编译器发出的 IL 代码大致转换为您提到的等效 C# 代码。
空值传播是一个非常好的功能 - 但是 where and how 真正的魔法真的发生了吗?
frm?.Close()
在哪里更改为 if(frm != null) frm.Close();
- 它实际上完全更改为那种代码吗?
这是编译器完成的。就 re-writing 源代码而言,它不会将 frm?.Close()
更改为 if(frm != null) frm.Close();
,但它 会 发出检查空值的 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
- 弹出堆栈的顶部值。如果为真,或者not-null(引用类型),则分支到IL_000B
如果结果为false(即对象为null)
pop
- 弹出堆栈(清除堆栈,我们不再需要 Person
的值)
ret
- Returns
如果值为真(即对象不为空)
call
- 在堆栈的 top-most 值上调用 DoIt()
(当前是 GetPerson
的结果)。
ret
- Returns
手动空检查:
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
Does it actually get changed to that kind of code at all?
嗯,是的,但在 IL 级别,而不是 C# 级别。编译器发出的 IL 代码大致转换为您提到的等效 C# 代码。