65.9K
CodeProject 正在变化。 阅读更多。
Home

.NET 中的 Equality 故事 - 第 4 部分

starIconstarIconstarIconstarIconstarIcon

5.00/5 (17投票s)

2016 年 7 月 9 日

CPOL

6分钟阅读

viewsIcon

25597

本文是对前三篇文章的延续,内容涉及 .NET 中 Equality 的工作方式,目的是让开发者更清楚地了解 .NET 如何处理类型的 Equality。

引言

我希望您在阅读了前三篇文章后,已经理解了 .NET 框架如何使用 virtual Object.Equals 方法以及对于大多数值类型和少数引用类型使用 IEquatable<T> 来处理 Equality。在本篇文章中,我们将讨论 Microsoft 提供的 C# Equality 运算符。我们将探讨 C# Equality 运算符的作用及其工作原理。阅读完本文后,我希望您能更好地理解当您使用 == 运算符检查两个变量是否相等时的含义。

我们将涵盖以下内容

  • 我们将编写一些代码来比较 == 运算符和 Object.Equals 对于相同参数的行为,看看会发生什么,我们将看到结果是相同的,但两者使用的机制是不同的。
  • 我们将看到 C# Equality 运算符如何处理基本类型。

C# 也提供了不相等运算符,其语法是 !=。我们不会详细讨论此运算符,因为它只是 Equality 运算符行为的否定,所以它们差别不大。例如,如果 a==b 的计算结果为 true,那么 a!=b 的计算结果应该为 false,反之亦然。除此差异外,这两个运算符的工作方式完全相同。因此,请记住,本文中关于 Equality 运算符的所有讨论也适用于不相等运算符,它只会反转返回值。

Equality 运算符用于我们想评估两个变量是否相等。许多开发人员有一个误解,认为此运算符与 Object.Equals 方法基本相同,只是 C# 语言提供的一种语法方便。

这实际上并不 true。它的设计方式常常通过调用 Object.Equals 来产生相同的结果,但这并非总是如此,因为底层机制完全不同。

背景

本文是对前三篇文章的延续,内容涉及 .NET 中 Equality 的工作方式,目的是让开发者更清楚地了解 .NET 如何处理类型的 Equality。您可能还想阅读之前的文章

== 运算符和基本类型

我们将通过示例代码了解 Equality 运算符相对于 Object.Equals 方法使用不同的机制。

    class Program
    { 
        static void Main(String[] args)
        {
            int num1 = 5;
            int num2 = 5;
 
            Console.WriteLine(num1.Equals(num2));
            Console.WriteLine(num1 == num2); 
 
            Console.ReadKey();
        } 
    }

我们将使用两种方式比较两个整数的相等性,第一种是使用整数的 Object.Equals 重载,第二种是使用 C# Equality 运算符,我们将检查生成的 IL 代码,这将帮助我们理解它们在机制上的不同。

当然,当我们运行此程序时,两个语句都会评估为 true,您也可以在自己的机器上进行测试。由于两个语句是相同的,这使我们相信它们都使用了 Object.Equals 并检查了两个整数的相等性。

幕后发生的事情

正如我们之前所谈到的,== 运算符和 Object.Equals 的工作方式不同,我们将看看它是如何不同的,这将证明我们之前所说的。我们将检查编译后两个语句生成的 IL。

这里要注意的一点是,在执行此操作之前,您需要使用 Release 版本而不是 Debug 版本构建项目,因为 Debug 代码会生成大量不必要的指令,这些指令在我们需要调试时很有用,并且在 Debug 版本中,它将同时使用 Object.Equals 实现,所以这无助于您看到我们接下来将要讨论的内容。

要做到这一点,请打开 Visual Studio 命令提示符,打开它的方法是,转到 开始菜单 >> 所有程序 >> Microsoft Visual Studio >> Visual Studio Tools >> Developer Command Prompt

在命令提示符中键入 ildasm,这将启动 ildasm,它用于查看程序集中包含的 IL 代码,它会在安装 Visual Studio 时自动安装,所以您无需进行任何安装操作。

使用文件菜单浏览可执行文件所在的文件夹并打开它。这将显示可执行文件的 IL 代码。

展开 Program 类并双击 Main 方法。它将打开 Main 方法的中间语言。

您不需要理解其中的所有代码,如果您以前从未见过 IL,它可能会让您感到复杂,但您不需要理解所有指令。

我们将只查看用于两种方式进行比较的行,以向您展示区别,您可以看到以下行

pIL_0007:   call       instance bool [mscorlib]System.Int32::Equals(int32)

这里,它正在调用通过 IEquatable<int> 为整数类型提供的 Object.Equals 实现。在 IL 中,我们需要使用完全限定名来指定方法调用,所以上面的语句的意思是调用一个名为 Equals 的方法,该方法接受 int32 作为参数,并且该方法存在于 System.Int32 类型中,而该类型存在于 mscorlib 程序集中。

现在看一下为第二次比较生成的 IL,该比较是通过 Equality 运算符完成的,如下所示:

IL_0013:  ceq

您可以看到,在这种情况下,没有调用 Int 类中的 Equals 方法,而是有一个 IL 指令 ceq ,它表示比较当前堆栈上的两个值,并使用 CPU 寄存器执行相等性比较。因此,C# Equality 运算符使用 ceq 语句来完成基本类型的相等性检查,而不调用该基本类型提供的 Object.Equals 实现。

摘要

  • 我们在此文中比较了 == 运算符与 Object.Equals 方法。
  • 我们发现,使用 == 运算符得到的结果与调用 Object.Equals 相同,但 == 运算符在 IL 中的底层机制与 Object.Equals 不同,即它不使用 Object.Equals,而是可能使用 CPU 寄存器来进行比较。

您可能还想阅读

© . All rights reserved.