自学者请教 关于 c#对象值相等为什么要重写 Equals(object obj)的问题,自己查了几个小时资料也没解决

新手上路,请多包涵

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

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 值相等?

阅读 2.6k
3 个回答

因为所有类型都继承自 System.Object,你重写的是这个父类里的方法。

另外 Object 有一个静态方法 ReferenceEquals() 来判断两个对象是否相等,这里面会隐式调用 Equals(object obj),所以你要重写。

再一个你写的这个其实是不对的,既然重写了 Equals(),就一定也要同时重写 GetHashCode()


最后一个问题仅给关键代码参考吧:

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

    public bool Equals(Pet obj)
    {
        return object.ReferenceEquals(obj, this) ||
            (!(obj is null) && this.Name == obj.Name && this.Age == obj.Age);
    }
    
    public bool Equals(Gdd obj)
    {
        return !(obj is null) && this.Name == obj.Name && this.Age == obj.Age;
    }

    public override bool Equals(object obj)
    {
        return Equals(obj as Pet) || Equals(obj as Gdd);
    }
    
    public override int GetHashCode()
    {
        // 随便写的 Hash 算法,保证当两个不相等的对象 HashCode 一定不相等就可以了
        int accumulator = 0;
        
        if (Name != null)
        {
            accumulator = Name.GetHashCode();
        }
        else
        {
            accumulator |= (0 & 0x0000000F) << 28
        }

        accumulator |= (Age & 0x000000FF) << 20;
        return accumulator;
    }
}

以上代码是徒手写的,未经过测试,可能有谬误,自行理解一下吧。

我的想法是,假设Pet对象不是以Pet类型得到的,那么Equals(Object)就有用武之地。Object是所有类型的基类,保证了两个对象不论以何种类型呈现,都可以被正确地比较。

如果你不重写这个,对于很多调用它的地方都有影响。

很简单例子……比如你可能会把它放在了一个 System.Collections.ArrayList 里,然后 contains(Object) 就取不到了。

class MyClass
{
    public bool Equals(MyClass c) => true;
}
public static void Main()
{
    ArrayList arl = new ArrayList();

    MyClass myClass = new MyClass();
    MyClass myClass2 = new MyClass();

    arl.Add(myClass);
    Console.WriteLine(arl.Contains(myClass2)); // 输出 False
}

其实不管怎么样,我们应当首先认为判断相等其实应该重写 Equals(object obj) 。因为……既然别人完全可以调用到这个 Equals(object obj) ,你至少要保证功能正常吧?随后我们才说,再写个 Equals(Pet pet) 更好。同时你也应该让它实现一个 System.IEquatable<Pet>


第二个问题没怎么看懂……

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
logo
Microsoft
子站问答
访问
宣传栏