我假设您正在查看 .NET 3.5 实现?我相信 .NET 4 的实现略有不同。
但是,我有一个偷偷摸摸的怀疑,这是因为甚至可以在 null 引用上非虚拟地调用虚拟实例方法。在 IL 中是可能的,也就是说。我会看看我是否可以产生一些会调用null.Equals(null)
.
编辑:好的,这里有一些有趣的代码:
.method private hidebysig static void Main() cil managed
{
.entrypoint
// Code size 17 (0x11)
.maxstack 2
.locals init (string V_0)
IL_0000: nop
IL_0001: ldnull
IL_0002: stloc.0
IL_0003: ldloc.0
IL_0004: ldnull
IL_0005: call instance bool [mscorlib]System.String::Equals(string)
IL_000a: call void [mscorlib]System.Console::WriteLine(bool)
IL_000f: nop
IL_0010: ret
} // end of method Test::Main
我通过编译以下 C# 代码得到了这个:
using System;
class Test
{
static void Main()
{
string x = null;
Console.WriteLine(x.Equals(null));
}
}
...然后反汇编ildasm
和编辑。注意这一行:
IL_0005: call instance bool [mscorlib]System.String::Equals(string)
最初,那是callvirt
代替call
.
那么,当我们重新组装它时会发生什么?好吧,使用 .NET 4.0 我们得到了这个:
Unhandled Exception: System.NullReferenceException: Object
reference not set to an instance of an object.
at Test.Main()
唔。.NET 2.0 怎么样?
Unhandled Exception: System.NullReferenceException: Object reference
not set to an instance of an object.
at System.String.EqualsHelper(String strA, String strB)
at Test.Main()
现在这更有趣了……我们显然已经设法进入EqualsHelper
,这是我们通常不会预料到的。
字符串够了……让我们自己尝试实现引用相等,看看我们是否可以null.Equals(null)
返回 true:
using System;
class Test
{
static void Main()
{
Test x = null;
Console.WriteLine(x.Equals(null));
}
public override int GetHashCode()
{
return base.GetHashCode();
}
public override bool Equals(object other)
{
return other == this;
}
}
与以前相同的程序 - 拆卸,更改callvirt
,call
重新组装,并观看它打印true
......
请注意,虽然另一个答案引用了这个 C++ 问题,但我们在这里更加狡猾......因为我们以非虚拟方式调用虚拟方法。通常,即使是 C++/CLI 编译器也会使用callvirt
虚拟方法。换句话说,我认为在这种特殊情况下,为this
null 的唯一方法是手动编写 IL。
编辑:我刚刚注意到一些事情......我实际上并没有在我们的任何一个小示例程序中调用正确的方法。这是第一种情况下的调用:
IL_0005: call instance bool [mscorlib]System.String::Equals(string)
这是第二个电话:
IL_0005: call instance bool [mscorlib]System.Object::Equals(object)
在第一种情况下,我打算打电话System.String::Equals(object)
,而在第二种情况下,我打算打电话Test::Equals(object)
。从中我们可以看出三点:
- 你需要小心超载。
- C# 编译器向虚拟方法的声明者发出调用,而不是对虚拟方法的最具体的覆盖。IIRC,VB的工作方式相反
object.Equals(object)
很高兴比较一个空的“this”引用
如果您在 C# 覆盖中添加一些控制台输出,您会看到差异 - 除非您更改 IL 以显式调用它,否则它不会被调用,如下所示:
IL_0005: call instance bool Test::Equals(object)
所以,我们到了。有趣和滥用空引用上的实例方法。
如果您已经做到了这一点,您可能还想查看我的博客文章,了解值类型如何在IL 中声明无参数构造函数。