在'CLR via C#'的第170页:
public sealed class Program {
public Int32 GetFive() { return 5; }
public static void Main() {
Program p = null;
Int32 x = p.GetFive(); // In C#, NullReferenceException is thrown
}
}
理论上,上面的代码很好。当然,变量p为null,但在调用非虚拟时 方法(GetFive),CLR只需要知道p的数据类型,即Program。如果 GetFive确实被调用了,这个参数的值将为null。自从争论 在GetFive方法中没有使用,不会抛出NullReferenceException。
答案 0 :(得分:5)
CLR不会对非虚方法进行空检查。基本上,如果使用call
指令调用方法,则CLR不会检查空this
指针。相反,callvirt
指令始终检查无效。但是,无论方法是否为虚拟,C#都会发出callvirt
指令。
该段落的内容是,如果C#编译器为非虚方法发出了语义上更合适的call
指令而不是callvirt
指令,那么有问题的代码就不会抛出{{ 1}}。我记得,编译器团队决定几乎总是发出NullReferenceException
指令,因为它更好地处理版本控制(JIT也可以将callvirt
优化为callvirt
)。
答案 1 :(得分:3)
this
指的是它自身的当前实例(该类)。
您的代码段,
Program p = null;
Int32 x = p.GetFive(); // In C#, NullReferenceException is thrown
无效,因为您正在尝试调用GetFive
null
的方法Program
,这是一个不存在的{{1}}实例 - 换句话说,您正试图敲门一个虚空,一扇不存在的门。由于CLR不知道门的位置,因此抛出异常“无法找到功能门!”对你而言 - 比未定义的行为要好得多。
答案 2 :(得分:0)
好。我只是通过C#,第3版查阅了CLR第170页。
也许页面的重点是 重要 部分,其中另一个CLR语言编译器生成一些使用您的C#类的代码,然后您将C#代码更改为一个非虚方法,不重新编译引用C#库的代码。在这种情况下,可能会出现问题,具体取决于调用者是实现call还是callvirt(未定义编译器将执行的操作)。
c#总是默认为callvirt,所以没问题,但对于来电者你不能提前知道。如果你这样做,如果你要运送图书馆或API,你可能会无意中破坏别人的程序。
试试这个。
public static Int32 GetFive() { return 5; }
public static void Main() {
Int32 x = GetFive();
}