自己尝试写出如下代码,来判断对象相等,虽然能正常工作。
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)。
官方说实现特定的 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; }
    }
}
从逻辑上来说,a1 和 a2 的值是相等的,此时调用的是 Equals(object obj),不过永远返回 false。
如以上代码中完全独立的两个类,怎么写 Equals 方法去判断 a1 和 a2 值相等?
|  |      16IbA2bj5ip3tK49j      2019-10-26 20:26:25 +08:00 via iPhone 自己搜索下,java 这种问题面试都被问吐了。c#估计也差不多。 | 
|  |      2cmdOptionKana      2019-10-26 20:42:00 +08:00  1 一般你要比较两个对象,是不能只用 public bool Equals(Pet other)的,因为那个 other 可能不是 Pet。 如果你只有 public bool Equals(Pet other),那么当你喂给他一个不是 Pet 的东西时,就会有问题了。 | 
|      3charlie21      2019-10-26 22:01:56 +08:00 via iPhone | 
|      4charlie21      2019-10-26 22:12:10 +08:00 via iPhone 软件开发里有很多邪门儿的事情是你不能控制也试验不出来的 ,尤其是 标准库 / 语言内置模块 / 语言 SDK 里的东西,晓得吧 。已有崖追无涯,殆矣  邪门儿玩意呢 往好了说叫 灵魂 ,往坏了说就是 黑魔法,作为语言研究者 你可以去研究(丰富知识),作为语言使用者 用最简单无脑的方式达到目的是赢 (节省时间) | 
|      5charlie21      2019-10-26 22:12:50 +08:00 via iPhone *灵魂 -> 灵活 | 
|  |      6ColinZeb      2019-10-26 22:17:07 +08:00 via iPhone 这是为了实现装箱比较吧,c#坑挺少的,不像 java 那样小坑大坑远古巨坑都特别多。我面试的时候见过很多面试用 java 的坑来套路面试者,很搞笑。 | 
|  |      7darktone OP @cmdOptionKana 是不是说,重写 Equals(object obj)目的是为了更好的兼容性? 但是我喂一个不是 pet 的东西,比如 Gdd,就算调用了 Equals(object obj)执行到 Pet p = obj as Pet; if ((Object)p == null) { return false; 这里就返回 false 了,也不会去执行 return this.Name == p.Name && this.Age == p.Age; Equals(object obj)中这样写有啥意义呢? | 
|  |      8darktone OP | 
|  |      9cmdOptionKana      2019-10-26 23:01:48 +08:00 @darktone 类型不同就应该返回 false,没必要进一步比较了。 | 
|      10charlie21      2019-10-26 23:04:37 +08:00  1 那你需要重新考虑你的程序是否对 object 的 “类型” 那么敏感。实际上 它就是一套方法集的名字而已 ,在 OOP 的视角 叫做类,在 Procedural 的视角 叫做 module ( 根本不考虑 OOP 那一套 )    https://en.wikipedia.org/wiki/Procedural_programming | 
|  |      11cmdOptionKana      2019-10-26 23:10:55 +08:00 @darktone 按照传统的 OOP 思想,你要先判断类型是否相同。charlie21  说的也是一种方法,具体看实际情况选择处理方式。 不过刚入门还是建议先理解传统 OOP 思想,以后了解别的模式,不然一下子接受一堆思想很难消化。 | 
|      12charlie21      2019-10-26 23:16:23 +08:00 它的相等和你的相等是一回事吗?你这个需求看起来实际上是想要自己定义何谓相等 ( “从逻辑上来说,a1 和 a2 的值是相等的”,这里你已经对你的 “相当” 有了明确的定义 )。那么你完全可以考虑自己定义 何谓 “相等” 并且直接去做。 | 
|  |      13yejinmo      2019-10-26 23:48:51 +08:00 | 
|  |      14geelaw      2019-10-26 23:54:38 +08:00 考虑代码 (new Pet()).Equals((object)(new Pet())) 它调用的是 Pet.Equals(object other) 方法而不是 Pet.Equals(Pet other) 方法,因为重载决议是在编译时间进行的。 一个最简单的写法是 public override bool Equals(object other) { return Equals(other as Pet); } 另外 if ((Object) other ==null) 里面的类型转换是多余的。 | 
|  |      15geelaw      2019-10-26 23:56:38 +08:00 关于你的第二个疑惑,一般来说我们会希望这俩不相等才对。例如一个人和一只宠物,它们都可以有名字和年龄,但是通常我们不希望它们有“相等”的概念。 | 
|  |      16geelaw      2019-10-26 23:59:49 +08:00  2 @geelaw #14 另一个常见的情况是 ((object)(new Pet())).Equals(new Pet()) 这也会调用 Pet.Equals(object other),因为通过 object 引用的 Pet 只能访问到 object.Equals(object other) 的重写方法。 注意,这也涵盖了用接口引用访问的情形,例如 interface ISome { /* 不含有名字叫 Equals 的方法 */ } class Pet : ISome { ... } ISome pet = new Pet(); pet.Equals(pet); // 调用的是 Pet.Equals(object other) | 
|  |      17darktone OP 感谢各位的回复。 又写了点测试代码,加上查资料+反汇编,我基本上弄清楚了。 仅从题目来说,特定的 Equals(Pet other)和 Equals(object obj)并非必要,两者有一个即可。 微软之所以在文档示例中两者都显式写明实现,是为了“泛用性”,“一致性”。 用户自己写的代码,当然可以显式指定去调用特定的 Equals(Pet other),但 net framework 中还有许多用户并没有显式指明的部分,此时 net framework 都会默认使用 Equals(object obj),因为它不知道用户有没有实现特定的 Equals(Pet other),自然不能预先假设,但 net 有一点可以确定,哪怕用户没有自定义的 overwrite Equals(object obj),也有基类 object Equals(object obj)可用,不会产生异常。 | 
|  |      19geelaw      2019-10-30 04:22:07 +08:00 @darktone #17 这个理解是完全没有搞清楚。你这样写会导致很多 implicit invariant 失效,从而程序虽然可以运行,但是意思却不是你想的那样,虽然没有产生 exception,但是几乎一定是错误的程序。 通常我们希望 object.Equals(object other) 的重写方法是判断对象相等性,如果你不重写,对象的相等性会被理解为同一性。 另外“(方法)重写”是 override 而不是 overwrite。 @charlie21 #18 object.Equals(object other) 的实现是 object.ReferenceEquals(this, other)。 |