在C#中,相等性操作符(==)和Equals方法在处理引用类型时的行为和原理是许多开发者所关心的话题。本文将深入探讨这两个概念在引用类型上的应用,以及它们在.NET框架中的实现细节。
本篇文章是关于.NET中相等性操作的系列文章之一。目的是帮助开发者更清晰地理解.NET如何处理不同类型的相等性。如果对这个话题感兴趣,可以从之前的部分开始阅读:
在之前的文章中,讨论了使用引用类型进行相等性比较时,==操作符是如何检查引用相等性的。现在,将修改相同的示例,看看在引用类型的情况下,==操作符是如何编译的。
class Program
{
static void Main(String[] args)
{
Person p1 = new Person();
p1.Name = "Ehsan Sajjad";
Person p2 = new Person();
p2.Name = "Ehsan Sajjad";
Console.WriteLine(p1.Equals(p2));
Console.WriteLine(p1 == p2);
Console.ReadKey();
}
}
现在,将使用这两种方法来检查相等性,即C#的相等性操作符和Object类型的Equals方法。如果运行这个示例,将看到控制台上两次打印出false,这是预期的结果,因为Person是一个引用类型,这两个是person的不同实例,具有不同的内存引用。
现在让检查一下这个示例生成的IL代码。要做到这一点,打开Visual Studio命令提示符,进入“开始菜单 >> 所有程序 >> Microsoft Visual Studio >> Visual Studio工具 >> 开发人员命令提示符”。在命令提示符中输入ildasm,这将启动ildasm,它用于查看程序集中包含的IL代码,它在安装Visual Studio时自动安装,所以不需要做任何安装工作。
浏览可执行文件所在的文件夹,并使用文件菜单打开它。这将显示可执行文件的IL代码。上述C#代码的IL代码如下:
// IL代码示例
如果查看p1.Equals(p2)的IL代码,没有什么惊喜,它通过调用Object的虚拟Equals方法来比较相等性,方法签名非常清晰,因为它们要求一个对象,所以这是C#编译器选择的最佳类型方法匹配。
现在,如果查看生成的对象相等性操作符比较的IL代码,它与在上一部分中看到的整数示例中使用的指令完全相同。它没有调用任何方法来进行比较,它只是将两个参数加载到评估堆栈上,并执行ceq,这是一个专用的IL指令,可能使用CPU硬件来检查相等性。
可能会想这如何实现引用相等性?知道Person是一个类,这是一个引用类型,这意味着Person类型的变量包含的是托管堆上Person对象的内存地址。
两个参数p1和p2都持有内存地址,知道内存中的地址只是数字,这意味着它们可以使用ceq语句进行相等性比较,就像在代码中声明的整数一样。实际上,这个ceq是在比较地址,看它们是否相等,换句话说,引用是否指向相同的内存地址,这就是引用相等性。