在IL中的空引用上调用实例方法是否正确? 有没有任何例子可以证明这一点..?
答案 0 :(得分:10)
是的,这是可能的,只要该方法不使用this
,因为CLR不对call
指令进行空检查。
您必须手动修改IL,因为C#编译器几乎总是生成callvirt
指令 1 。
有关详细信息和示例,请参阅此博客文章:
<强>示例强>
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
// Code size 18 (0x12)
.maxstack 1
.locals init ([0] class SomeClass o, [1] string hello)
IL_0000: nop
IL_0001: ldnull
IL_0002: stloc.0
IL_0003: ldloc.0
IL_0004: call instance string SomeClass::GetHello()
IL_0009: stloc.1
IL_000a: ldloc.1
IL_000b: call void [mscorlib]System.Console::WriteLine(string)
IL_0010: nop
IL_0011: ret
}
1 实际上,即使在简单的callvirt
指令足够的情况下,C#编译器发出call
的原因是为了防止在空引用上调用实例方法。使用编译器的这种行为,用户将获得NullReferenceException
,因此避免了在空指针上调用方法的奇怪情况。 Eric Gunnerson在一段时间的博客文章中对此进行了解释: Why does C# always use callvirt? Gishu在related question中也有一个很好的解释。< / p>
答案 1 :(得分:5)
请参阅我的blog entry了解相关信息。
IL代码示例:
.class Program
{
.method static void Main(string[] args)
{
.entrypoint
.locals init ([0] class Program p)
ldloc.0 // but wait, it's still null!
call instance void Program::Awesome()
ret
}
.method instance void Awesome()
{
ldstr "Awesome!"
call void [mscorlib]System.Console::WriteLine(string)
ret
}
.method public specialname rtspecialname instance void .ctor()
{
ldarg.0
call instance void [mscorlib]System.Object::.ctor()
ret
}
}
答案 2 :(得分:3)
CLR不需要它,它是该语言的实现细节。 C#和VB.NET执行测试。 C ++ / CLI是一种值得注意的托管语言。只要实例方法不引用任何类成员,那么什么都不会出错。如果是,NullReferenceException将正常引发,只是让你很难找到原因。
#include "stdafx.h"
using namespace System;
ref class Test {
public:
void Run() {
Console::WriteLine("no problem");
}
};
int main(array<System::String ^> ^args)
{
Test^ obj = nullptr;
obj->Run();
return 0;
}