偶尔我喜欢花些时间看电影.NET代码只是为了看看事情是如何在幕后实现的.我在通过反射器查看String.Equals方法时偶然发现了这颗Ruby .

C#

[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);
}

IL

.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 
}

thisnull进行对比的理由是什么?我必须假设这是有目的的,否则这可能已经被捕获并移除了.

推荐答案

我猜你是在看.NET 3.5的实现?我相信.NET4的实现略有不同.

然而,我暗自怀疑这是因为即使是虚拟实例方法也可以调用非虚拟on a null reference.在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()

嗯.那么.NET2.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强,这是我们通常不会想到的.

够了,绳子...让我们自己try 实现引用平等,看看是否可以得到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.

请注意,尽管另一个答案引用了this C++ question,但我们在这里更加狡猾……因为我们非虚拟地调用virtual方法.通常,即使是C++/CLI编译器也会使用callvirt作为虚方法.换句话说,我认为在这种情况下,this为空的唯一方法是手写IL.


编辑:我刚刚注意到一些事情...在我们的either个小样本程序中,我实际上没有调用正确的方法.以下是第一种情况下的情况:

IL_0005:  call instance bool [mscorlib]System.String::Equals(string)

下面是第二个中的调用:

IL_0005:  call instance bool [mscorlib]System.Object::Equals(object)

在第一种情况下,我meant拨打System.String::Equals(object),在第二种情况下,我meant拨打Test::Equals(object).从中我们可以看到三件事:

  • 你需要小心超载.
  • C#编译器向虚方法的第declarer个发出调用,而不是虚方法的最具体的override.IIRC,VB的工作方式正好相反
  • object.Equals(object)很乐意比较一个空的"this"引用

如果在C#override中添加一点控制台输出,就可以看到区别——除非将IL更改为显式调用它,否则不会调用它,如下所示:

IL_0005:  call   instance bool Test::Equals(object)

我们到了.null引用上实例方法的有趣和滥用.

如果你已经做到了这一点,你可能还想看看我的博客帖子大约how value types can declare parameterless constructors...在伊利诺伊州.

.net相关问答推荐

从窗体中移除另一个控件中引用的控件时获取设计时通知

为什么解码后的字节数组与原始字节数组不同?

查询 MongoDb 中嵌入式文档中的一个字段,该字段抛出调用运算符的左侧必须是对持久属性的直接访问

信号量的多线程问题

C#字符串的GetHashCode()是如何实现的?

什么是表达式树,如何使用它们,为什么要使用它们?

使用 Thread.Abort() 有什么问题

为什么 Any() 不适用于 c# null 对象

我可以从我的应用程序中抛出哪些内置 .NET 异常?

.NET 的 `Array.Sort()` 方法使用的排序算法是稳定的算法吗?

通用枚举到int的C#非装箱转换?

处理序列没有元素异常

运算符重载 ==, !=, Equals

如何退出所有正在运行的线程?

覆盖方法上的 C# 可选参数

如何将 XPath 与 XElement 或 LINQ 一起使用?

IEnumerable vs IReadonlyCollection vs ReadonlyCollection 用于公开列表成员

如何判断对象是否已在 C# 中释放

如何判断uri字符串是否有效

浮动与双重性能