鉴于这两种方法:
static void M1(Person p)
{
if (p != null)
{
var p1 = p.Name;
}
}
static void M2(Person p)
{
var p1 = p?.Name;
}
为什么使用M1 IL代码callvirt
:
IL_0007: brfalse.s IL_0012
IL_0009: nop
IL_000a: ldarg.0
IL_000b: callvirt instance string ConsoleApplication4.Person::get_Name()
和 M2 IL 使用call
:
brtrue.s IL_0007
IL_0004: ldnull
IL_0005: br.s IL_000d
IL_0007: ldarg.0
IL_0008: call instance string ConsoleApplication4.Person::get_Name()
我只能猜测,因为在 M2 中我们知道p
不为空及其类似
new MyClass().MyMethod();
这是真的吗?
如果是的话,如果p
在其他线程中会为空吗?
The callvirt
在 M1 中是标准 C# 代码生成 http://blogs.msdn.com/b/ericgu/archive/2008/07/02/why-does-c-always-use-callvirt.aspx。它提供了语言保证,永远不能使用空引用调用实例方法。换句话说,它确保p != null
如果为 null,则生成 NullReferenceException。您的显式测试不会改变这一点。
这个保证非常好,如果是的话,调试 NRE 会变得非常麻烦this
那是空的。相反,在调用站点诊断事故要容易得多,调试器可以快速向您表明它是p
那是麻烦制造者。
但是当然callvirt
不是免费的,尽管成本很低,运行时多一条处理器指令。所以如果它can被替换为call
那么代码将会快半纳秒,不管怎样。事实上,它可以与 elvis 运算符一起使用,因为它已经确保引用不为 null,因此 C# 6 编译器利用了这一点并生成 call 而不是 callvirt。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)