为什么要检查此!= null?

有时,我喜欢花一些时间查看.NET代码,以了解幕后如何实现事物。我在String.Equals通过Reflector 查看方法时偶然发现了这颗宝石。

[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]

public override bool Equals(object obj)

{

string strB = obj as string;

if ((strB == null) && (this != null))

{

return false;

}

return EqualsHelper(this, strB);

}

.method public hidebysig virtual instance bool Equals(object obj) cil managed

{

.custom instance void System.Runtime.ConstrainedExecution.ReliabilityContractAttribute::.ctor(valuetype System.Runtime.ConstrainedExecution.Consistency, valuetype System.Runtime.ConstrainedExecution.Cer) = { int32(3) int32(1) }

.maxstack 2

.locals init (

[0] string str)

L_0000: ldarg.1

L_0001: isinst string

L_0006: stloc.0

L_0007: ldloc.0

L_0008: brtrue.s L_000f

L_000a: ldarg.0

L_000b: brfalse.s L_000f

L_000d: ldc.i4.0

L_000e: ret

L_000f: ldarg.0

L_0010: ldloc.0

L_0011: call bool System.String::EqualsHelper(string, string)

L_0016: ret

}

什么是检查的理由this反对null?我必须假设有目的,否则到现在可能已经捕获并删除了它。

回答:

我假设您正在查看.NET 3.5的实现?我相信.NET 4的实现略有不同。

但是,我有一个偷偷摸摸的怀疑,这是因为甚至有可能 在null引用上

非虚拟地调用虚拟实例方法。即在IL中可能。我看看是否可以产生一些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;

}

}

与以前相同的过程-拆卸,更改callvirtcall,重新组装并观看打印true

请注意,尽管另一个答案引用了这个C ++问题,但我们在这里更加曲解……因为我们非 虚拟地 调用 虚拟 方法。通常,即使C ++ /

CLI编译器也将callvirt用于虚拟方法。换句话说,我认为在这种特殊情况下,唯一的为thisnull的方法是手动编写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。

以上是 为什么要检查此!= null? 的全部内容, 来源链接: utcz.com/qa/409427.html

回到顶部