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





5.00/5 (17投票s)
本文是对前三篇文章的延续,内容涉及 .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。您可能还想阅读之前的文章
- igualdad.NET 故事 - 第一部分
- NET 中相等性的故事(虚拟 Object.Equals、静态 Object.Equals 和 Reference.Equals)– 第二部分
- NET 中相等性的故事(IEquatable<T> 接口简介)– 第三部分
- NET 中相等性的故事(C# 中的 == 运算符和原始类型)– 第四部分
- Equality in .NET 的故事 (C# 中的 == 运算符和引用类型) – 第 5 部分
- Equality in .NET 的故事 (C# 中的 == 运算符和 String 类型) – 第 6 部分
- Equality in .NET 的故事 (C# 中的 Equality 运算符 (==) 和值类型) – 第 7 部分
- Equality in .NET 的故事 (C# 中的 Equality 运算符 (==) 继承和泛型问题) – 第 8 部分
- 在 C# 中为值类型实现相等性 - 第九部分
- 在 C# 中为引用类型实现相等性 - 第十部分
== 运算符和基本类型
我们将通过示例代码了解 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 寄存器来进行比较。
您可能还想阅读
- igualdad.NET 故事 - 第一部分
- NET 中相等性的故事(虚拟 Object.Equals、静态 Object.Equals 和 Reference.Equals)– 第二部分
- NET 中相等性的故事(IEquatable<T> 接口简介)– 第三部分
- NET 中相等性的故事(C# 中的 == 运算符和原始类型)– 第四部分
- Equality in .NET 的故事 (C# 中的 == 运算符和引用类型) – 第 5 部分
- Equality in .NET 的故事 (C# 中的 == 运算符和 String 类型) – 第 6 部分
- Equality in .NET 的故事 (C# 中的 Equality 运算符 (==) 和值类型) – 第 7 部分
- Equality in .NET 的故事 (C# 中的 Equality 运算符 (==) 继承和泛型问题) – 第 8 部分
- 在 C# 中为值类型实现相等性 - 第九部分
- 在 C# 中为引用类型实现相等性 - 第十部分