4

网上有很多文章列出了 C# JIT 在执行一段代码之前所做的优化。例如,MSDN 上的这篇文章谈到:

常量折叠、常量和复制传播、公共子表达式消除、循环不变量的代码运动、死存储和死代码消除、寄存器分配、方法内联、循环展开(带有小主体的小循环)。

我的问题是:JIT 编译器是否也处理无用的空检查?我找不到任何处理这个问题的来源。

在同一篇文章中,我读到:

由于 C# 语言规范确保对空对象引用的任何调用都会引发 NullReferenceException,因此每个调用站点都必须确保实例不为空。这是通过取消引用实例引用来完成的;如果它为空,它将生成一个错误,该错误会变成此异常。

所以,假设我写了一段这样的代码:

if (person != null)
{
    Console.WriteLine(person.Name);
}

再次调用第二person.Name次空检查,这显然是无用的,编译器可以将其删除。或不?

我读到在 Java 中这已经完成(这里这里这里的许多来源之间的一些来源)。

如果 C# 也这样做,您是否知道一些关于此的源或文档?

如果相反,C# 不这样做,你知道为什么吗?在 Java JIT 没有遇到的 .NET 环境中实现这样的功能是否存在内在困难?

4

1 回答 1

3

Null检查编译器(Roslyn,而不是 Jitter)在几种情况下完成的优化,当它完全保存时。

例如,当您使用?(猫王运算符)时。

IL_0006: stloc.0              // Pop a value from stack into local variable 0
IL_0007: ldloc.0              // Load local variable 0 onto stack
IL_0008: brtrue.s IL_000c     // Branch to target if value is non-zero (true), short form
IL_000a: br.s IL_0013         // Branch to target, short form
IL_000c: ldloc.0              // Load local variable 0 onto stack
IL_000d: call instance void Foo::Bar() // Call method indicated on the stack with arguments

另一个例子是这样的代码:

new Bar().Foo();

编译器为这call条指令生成而不是callvirt(这意味着,没有空检查this

在其他情况下,您不能确定它this不会为空。

无论如何,空值检查真的很快。

于 2016-12-06T21:55:14.287 回答