c# 对特定Equals和Equals(object obj)的疑惑与总结!

本文探讨了C#中重写Equals(Pet other)和Equals(object obj)的疑惑。作者通过代码示例展示了两种重写方式的用意,解释了在特定条件下为何需要重写Equals(object obj),尤其是在操作符重载和兼容性方面的考虑。最后,总结认为两者并非必须同时存在,但微软文档示例中包含两者是为了体现泛用性和一致性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

自己尝试写出如下代码,来判断对象相等,虽然能正常工作

using System;

namespace 比较相等
{
    class Program
    {
        static void Main(string[] args)
        {
            
            Pet a1 = new Pet {  Name = "Turbo", Age = 8 };
            Pet a2 = new Pet { Name = "Turbo", Age = 8 };

                 
            if (a1.Equals(a2))
                Console.WriteLine("相等");
            else
                Console.WriteLine("不相等");
           

            Console.Read();

        }
    }
    class Pet
    {
        public string Name { get; set; }
        public int Age { get; set; }
        public  bool Equals(Pet other)
        {
            if ((Object) other ==null)
            {
                return false;
            }
            return this.Name == other.Name && this.Age == other.Age ;
        }
           
    }

}

但是查网上的资料,发现微软官方和其他博客不仅写了 Equals(Pet other),还要重写 override Equals(object obj)。

疑惑 1:

官方说实现特定的 Equals(Pet other)是为了提高性能,这个我勉强能理解。但是为什么还要重写 Equals(object obj)呢? 它的意义何在? 或者说,什么样的情况下,明明已经有了 Equals(Pet other)不会调用 Equals(Pet other),而是去调用 Equals(object obj)?

我尝试模仿官方文档,在 Pet 类中添加了 Equals(object obj),为了强行去调用 Equals(object obj)(疑惑 1 ),还单独创建了一个类 Gdd

using System;

namespace 比较相等
{
    class Program
    {
        static void Main(string[] args)
        {
            

            Pet a1 = new Pet {  Name = "Turbo", Age = 8 };
            Gdd a2 = new Gdd { Name = "Turbo", Age = 8 };

          
       
            if (a1.Equals(a2))
                Console.WriteLine("相等");
            else
                Console.WriteLine("不相等");
           

            Console.Read();

        }
    }
    class Pet
    {
        public string Name { get; set; }
        public int Age { get; set; }
        public  bool Equals(Pet other)
        {
            if ((Object) other ==null)
            {
                return false;
            }
            return this.Name == other.Name && this.Age == other.Age ;
        }

 public override bool Equals(object obj)
        {
           

            if (obj == null )
            {
                return false;
            }


            Pet p = obj as Pet;
            if ((Object)p == null)
            {
                return false;
            }

            return this.Name == p.Name && this.Age == p.Age;
        }
           

    }
class Gdd
    {
        public string Name { get; set; }
        public int Age { get; set; }
    }

}

疑惑 2:

从逻辑上来说,a1 和 a2 的值是相等的,此时调用的是 Equals(object obj),不过永远返回 false。

如以上代码中完全独立的两个类,怎么写 Equals 方法去判断 a1 和 a2 值相等?

猜测

个人猜测,如果仅仅是完成一个判断对象相等,只需要特定Equals(Pet other),Equals(object obj)非必须。
它分两种情况,一种传递的参数other是Pet型时,自然会调用该函数。一种传递的参数other不是Pet型,那么会去调用默认的object.Equals,这个方法判断的是引用地址,地址不同,永远返回false,隐藏含义:“类型都不同的对象值也就不相等”。

考虑用于操作符重载的情况,当重载==和!=时,表达式两边会有两种情况,

  public static bool operator==(Pet a,Pet b)
        {
            
            return a.Equals(b);
        }
        public static bool operator !=(Pet a, Pet b)
        {
            return !a.Equals(b);
        }

这样写,表达式只能写成类似 Pet a1== Pet a2,如果写成Pet a1== 3,因为3不属于Pet型,重载的==不支持这种写法。
代码直接标红无法编译。
当然这样写,功能可用,Equals(object obj)也是非必须。
为了更加符合兼容性的写法是:

 public static bool operator==(Pet a,object b)
        {
            
            return a.Equals(b);
        }
        public static bool operator !=(Pet a, object b)
        {
            return !a.Equals(b);
        }

Pet a1== 3,因为3属于object型,重载==操作符就兼容性的支持这种写法。

此时表达式的右侧都会被当成object,哪怕a2是Pet型也一样。
只有当以上的情况发生后,重写Equals(object obj)才是必须的,否则调用时会出现小bug。
①Pet a1== Pet a2 ,a2被当成object,需要在自己的a.Equals(object b)里判断,执行

return this.Name == p.Name && this.Age == p.Age;

不能再使用默认的object.Equals。
②Pet a1Gdd g1 或者Pet a1 3,g1或者3 被当成object,也需要在自己的a.Equals(object b)里判断,执行

 Pet p = obj as Pet;
            if ((Object)p == null)
            {
                return false;
            }

返回值为false。
很显然,如果操作符重载成泛用性 operator==(Pet a,object b),只用操作符判断,那特定的Equals就非必须了。

总结

我想来想去,都没想明白,为什么微软官方要同时写特定Equals和Equals(object obj),明明只需要二者之一就行。
没找到必须两者共存的场景。

又写了点测试代码,加上查资料,我基本上弄清楚了。
仅从题目来说,特定的Equals(Pet other)和 Equals(object obj)并非必要,两者有一个即可。

微软之所以在文档示例中两者都有,是为了“泛用性”,“一致性”。

用户自己写的代码,当然可以显式指定去调用特定的Equals(Pet other),但.net framework中还有许多用户并没有显式指明的部分,此时.net都会默认使用 Equals(object obj),因为它不知道用户有没有实现特定的Equals(Pet other),自然不能预先假设,但.net有一点可以确定,哪怕用户没有overwrite Equals(object obj),也有基类object. Equals(object obj)可用,不会产生异常。

using System;
using System.Diagnostics;
using System.Collections;
using System.Linq;

namespace 比较相等
{
    class Program
    {
        static void Main(string[] args)
        {


            Pet a1 = new Pet { Name = "Turbo", Age = 8, ID = 1 };
          
            Pet a3 = new Pet { Name = "Turbo", Age = 8, ID = 2 };
            Pet[] pets = { a1, a3 };
            Pet[] pets2 = { a3, a1 };
            if (pets.SequenceEqual(pets2))
            {
                Console.WriteLine("true");
            }
            else
            { Console.WriteLine("false"); }


            Console.Read();

        }
    }
    class Pet
    {
        public string Name { get; set; }
        public int Age { get; set; }
        public int ID { get; set; }
        public bool Equals(Pet other)
        {
            if ((Object)other == null)
           {
                return false;
            }
            return this.Name == other.Name && this.Age == other.Age&&this.ID==other.ID;
        }


        public override bool Equals(object obj)
        {


            if (obj == null)
            {
                return false;
            }


            Pet p = obj as Pet;
            if ((Object)p == null)
            {
                return false;
            }

            return this.Name == p.Name && this.Age == p.Age;
        }


        public override int GetHashCode()
        {
           
            return base.GetHashCode();
        }

    }
}    

判断的时候.net根本不会去调用特定的Equals(Pet other),而是调用overwrite Equals(object obj),运行结果是两个数组相等。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值