NullReferenceException可能是.Net程序员遇到最多的例外了, 这个例外发生的如此频繁,以至于人们付出了巨大的努力来使用各种特性和约束试图防止它发生, 但时至今日它仍然让很多程序员头痛, 今天我将讲解这个令人头痛的例外是如何发生的.
可以导致NullReferenceException发生的源代码
我们先来看看什么样的代码可以导致NullReferenceException发生:
第一份代码, 调用函数时this等于null导致例外发生
using System;
namespace ConsoleApp1{
class Program
{
public class MyClass
{
public int MyMember;
public void MyMethod() { }
}
static void Main(string[] args) {
MyClass obj = null;
obj.MyMethod();
}
}
}
第二份代码, 访问成员时this等于null导致例外发生
using System;
namespace ConsoleApp1{
class Program
{
public class MyClass
{
public int MyMember;
public void MyMethod() { }
}
static void Main(string[] args) {
MyClass obj = null;
Console.WriteLine(obj.MyMember);
}
}
}
观察生成的IL代码
再来看看生成的IL:
第一份代码的IL
.method private hidebysig static
void Main (
string[] args
) cil managed
{
// Method begins at RVA 0x2050
// Code size 11 (0xb)
.maxstack 1
.entrypoint
.locals init (
[0] class ConsoleApp1.Program/MyClass
)
IL_0000: nop
IL_0001: ldnull
IL_0002: stloc.0
IL_0003: ldloc.0
IL_0004: callvirt instance void ConsoleApp1.Program/MyClass::MyMethod()
IL_0009: nop
IL_000a: ret
} // end of method Program::Main
第二份代码的IL
.method private hidebysig static
void Main (
string[] args
) cil managed
{
// Method begins at RVA 0x2050
// Code size 16 (0x10)
.maxstack 1
.entrypoint
.locals init (
[0] class ConsoleApp1.Program/MyClass
)
IL_0000: nop
IL_0001: ldnull
IL_0002: stloc.0
IL_0003: ldloc.0
IL_0004: ldfld int32 ConsoleApp1.Program/MyClass::MyMember
IL_0009: call void [System.Console]System.Console::WriteLine(int32)
IL_000e: nop
IL_000f: ret
} // end of method Program::Main
看出什么了吗? 看不出吧, 我也看不出, 这代表了null检查不是在IL层面实现的, 我们需要继续往下看.
观察生成的汇编代码
看生成的汇编代码:
第一份代码生成的汇编 (架构不同生成的代码也不同, 以下代码是windows x64生成的)
10: static void Main(string[] args) {
00007FF9F5C30482 56 push rsi 00007FF9F5C30483 48 83 EC 30 sub rsp,30h 00007FF9F5C30487 48 8B EC mov rbp,rsp 00007FF9F5C3048A 33 C0 xor eax,eax 00007FF9F5C3048C 48 89 45 20 mov qword ptr [rbp+20h],rax 00007FF9F5C30490 48 89 45 28 mov qword ptr [rbp+28h],rax 00007FF9F5C30494 48 89 4D 50 mov qword ptr [rbp+50h],rcx 00007FF9F5C30498 83 3D 49 48 EA FF 00 cmp dword