C#中对象的相等性与同一性的判断方法总结

本文详细解释了C#中对象的相等性和同一性的区别,探讨了Equals()、ReferenceEquals()方法的工作原理,并通过实例展示了如何重写Equals()方法以及何时使用String的判等。
摘要由CSDN通过智能技术生成


在这里插入图片描述

1. 概述与准备

1.1 概述

在C#中,对象的相等性(equality)和同一性(identity)是两个不同的概念。

  • 相等性(Equality):相等性指的是两个对象的值是否相等。

    • 对于引用类型,默认情况下相等性比较的是对象的引用,即两个对象是否引用同一个内存地址;但可以通过重写 Equals 方法来自定义相等性比较的逻辑。
    • 对于值类型,相等性比较的是对象的值。
  • 同一性(Identity):同一性指的是两个对象是否是同一个实例。

    • 如果两个对象引用同一个内存地址,则它们是同一实例;
    • 否则,它们是不同的实例。

其实,这里涉及不少的值得关注的点,否则稍微不慎,便会出错。

这篇文章便总结一下**==操作符**、EqualReferenceEquals等等方法,以便更好地把握C#对象的相等性同一性

1.2 准备

为了方便后面举例子,这里先定义两个类:学生类学校类

class Student
{
    public int StuID { get; set; }
    public string StuName { get; set; }
    public School StuSchool { get; set; }

    public Student(int stuID, string stuName, School stuSchool)
    {
        this.StuID = stuID;
        this.StuName = stuName;
        this.StuSchool = stuSchool;
    }
}

class School
{
    public string SchoolName { get; set; }
    public string SchoolAddress { get; set; }

    public School(string name, string address)
    {
        this.SchoolName = name;
        this.SchoolAddress = address;
    }
}

2. Equals(Object)

2.1 功能:

System.Object类型提供了名为Equals的虚方法,作用是判断指定对象是否等于当前对象。此方法比较的是对象的引用

public virtual bool Equals (object? obj);

2.2 实例:

Student stu1 = new Student(001, "孙悟空", new School("清华大学", "北京市"));
Student stu2 = new Student(002, "猪八戒", new School("北京大学", "北京市"));
Student stu3 = new Student(001, "孙悟空", new School("清华大学", "北京市")); //与stu1属性值相同

//输出False。Equals方法比较的是对象的引用,而stu1和stu2是两个不同的对象,因此它们的引用不同。
Console.WriteLine(stu1.Equals(stu2)); 

//输出False。尽管stu1 和stu3的属性值相同,但它们仍然是两个不同的对象,因此它们的引用不同。
Console.WriteLine(stu1.Equals(stu3));

总结:对于Object的Equals方法的默认实现,它实现的实际是同一性(identity),而非相等性(equality)。这是易混淆的点!

2.3 扩展:

在 C# 中,== 操作符对于引用类型默认比较的是对象的引用,而不是属性值。stu1 和 stu2 是两个不同的对象,所以它们的引用不同,结果为 False。

Student stu1 = new Student(001, "孙悟空", new School("清华大学", "北京市"));
Student stu2 = new Student(002, "猪八戒", new School("北京大学", "北京市"));
Student stu3 = new Student(001, "孙悟空", new School("清华大学", "北京市")); //与stu1属性值相同

Console.WriteLine(stu1 == stu2); //输出False
Console.WriteLine(stu1 == stu3); //输出False

2.4 重写此方法

上面讲到,此方法是一个虚方法,这意味着其派生类可以重写此类。如果你想的话,你可以在Student类中增加下面的代码,以至于实现相等性而非同一性,即能够按对象的属性值进行比较:

public override bool Equals(object? obj)
{
    if (obj == null && this.GetType() != obj.GetType())
        return false;

    Student other = (Student)obj;
    return this.StuID == other.StuID &&
           this.StuName == other.StuName &&
           this.StuSchool.Equals(other.StuSchool);
}
注意,上述代码中的 StuSchool 的判等取决于StuSchool.Equals()是否被重写。

3. Equals(Object, Object)

3.1 功能

这是Object类的静态方法,也是比较类对象的引用

public static bool Equals (object? objA, object? objB);

这里分几种情况:

  1. objA 与 objB 都为null,返回false;
  2. objA 与 objB 仅有一个为null,返回false;
  3. objA 与 objB 都不为null,调用objA.Equals(objB)并返回结果。

3.2 实例

Student stu1 = new Student(001, "孙悟空", new School("清华大学", "北京市"));
Student stu2 = new Student(002, "猪八戒", new School("北京大学", "北京市"));
Student stu3 = new Student(001, "孙悟空", new School("清华大学", "北京市")); //与stu1属性值相同

Console.WriteLine(Object.Equals(stu1, stu2)); //输出False。这里实际上调用stu1.Equals(stu2);
Console.WriteLine(Object.Equals(stu1, stu3)); //输出False。这里实际上调用stu1.Equals(stu3);

4. ReferenceEquals(Object, Object)

4.1 功能

比较指定的 Object 实例是否是相同的实例,比较对象的引用。

public static bool ReferenceEquals (object? objA, object? objB);

注意:此方法不可重写

4.2 使用场景:

由于类型能够重写Equal方法,所以不能再用它测试同一性。为了解决这个问题,Object提供了另一个静态方法ReferenceEquals。

如果要测试两个对象引用是否相等,并且你不确定该类 Equals 方法是否被重写了,则可以调用此方法。

Student stu1 = new Student(001, "孙悟空", new School("清华大学", "北京市"));
Student stu2 = new Student(002, "猪八戒", new School("北京大学", "北京市"));
Student stu3 = new Student(001, "孙悟空", new School("清华大学", "北京市")); //与stu1属性值相同

Console.WriteLine(Object.Equals(stu1, stu2));//输出False
Console.WriteLine(Object.ReferenceEquals(stu1, stu2)); //输出False

当你确定该类的Equals方法没有被重写,那这两个方法几乎是一样的。

4.3 要注意的地方

请注意,我为什么说“几乎是一样呢”?理由简单,因为有不一样的地方:

int a = 1;

Console.WriteLine(a == a); //输出True。因为 == 操作符比较的是 int 类型的值。

Console.WriteLine(a.Equals(a)); //输出True。因为在int类型中,Equals方法被重写成比较int类型的值。

Console.WriteLine(Object.Equals(a, a)); //输出True。同上,这里会调用int类型的重写的Equals方法进行值的比较。

Console.WriteLine(Object.ReferenceEquals(a, a)); //输出False。由于 a 是 int 类型的值类型,它在比较过程中会被装箱为一个对象。因此,两个 a 的引用是不同的,结果为 False。

总结:

  • 比较值类型时,如果 objA 并且 objB 是值类型,则会在将它们传递到 ReferenceEquals 方法之前进行装箱。这意味着,如果同时objA与objB表示值类型的同一实例,ReferenceEquals该方法仍返回false。
  • 检查引用类型对象的同一性时建议调用ReferenceEquals,不建议使用==或者Equals。

5. 扩展:字符串的判等

string s1 = "Hello";

Console.WriteLine(s1 == s1); //输出True

Console.WriteLine(s1.Equals(s1)); //输出True。在string类型中Equals方法被重写,用于比较字符串的内容

Console.WriteLine(Object.Equals(s1, s1)); //输出True。同上。

Console.WriteLine(Object.ReferenceEquals(s1, s1)); //输出True。因为两个s1的引用是相同的。

注意:

  • 字符串常量会被存储在一个字符串池(string pool)中,以便重复使用。
  • 当创建两个具有相同内容的字符串时,它们实际上会引用相同的字符串对象。

如下:

string s2 = "Hello";

Console.WriteLine(s1 == s2); //输出True

Console.WriteLine(s1.Equals(s2)); //输出True。在string类型中Equals方法被重写,用于比较字符串的内容

Console.WriteLine(Object.Equals(s1, s2)); //输出True。同上。

Console.WriteLine(Object.ReferenceEquals(s1, s2)); //输出True。由于值相同,所以s1与s2引用相同的字符串对象。

参考

https://learn.microsoft.com/zh-cn/dotnet/api/system.object?view=net-7.0

  • 22
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值