自己尝试写出如下代码,来判断对象相等,虽然能正常工作。
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 值相等?
1
6IbA2bj5ip3tK49j 2019-10-26 20:26:25 +08:00 via iPhone
自己搜索下,java 这种问题面试都被问吐了。c#估计也差不多。
|
2
cmdOptionKana 2019-10-26 20:42:00 +08:00 1
一般你要比较两个对象,是不能只用 public bool Equals(Pet other)的,因为那个 other 可能不是 Pet。
如果你只有 public bool Equals(Pet other),那么当你喂给他一个不是 Pet 的东西时,就会有问题了。 |
3
charlie21 2019-10-26 22:01:56 +08:00 via iPhone
|
4
charlie21 2019-10-26 22:12:10 +08:00 via iPhone
软件开发里有很多邪门儿的事情是你不能控制也试验不出来的 ,尤其是 标准库 / 语言内置模块 / 语言 SDK 里的东西,晓得吧 。已有崖追无涯,殆矣
邪门儿玩意呢 往好了说叫 灵魂 ,往坏了说就是 黑魔法,作为语言研究者 你可以去研究(丰富知识),作为语言使用者 用最简单无脑的方式达到目的是赢 (节省时间) |
5
charlie21 2019-10-26 22:12:50 +08:00 via iPhone
*灵魂 -> 灵活
|
6
ColinZeb 2019-10-26 22:17:07 +08:00 via iPhone
这是为了实现装箱比较吧,c#坑挺少的,不像 java 那样小坑大坑远古巨坑都特别多。我面试的时候见过很多面试用 java 的坑来套路面试者,很搞笑。
|
7
darktone 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)中这样写有啥意义呢? |
8
darktone OP |
9
cmdOptionKana 2019-10-26 23:01:48 +08:00
@darktone 类型不同就应该返回 false,没必要进一步比较了。
|
10
charlie21 2019-10-26 23:04:37 +08:00 1
那你需要重新考虑你的程序是否对 object 的 “类型” 那么敏感。实际上 它就是一套方法集的名字而已 ,在 OOP 的视角 叫做类,在 Procedural 的视角 叫做 module ( 根本不考虑 OOP 那一套 )
https://en.wikipedia.org/wiki/Procedural_programming |
11
cmdOptionKana 2019-10-26 23:10:55 +08:00
@darktone 按照传统的 OOP 思想,你要先判断类型是否相同。charlie21 说的也是一种方法,具体看实际情况选择处理方式。
不过刚入门还是建议先理解传统 OOP 思想,以后了解别的模式,不然一下子接受一堆思想很难消化。 |
12
charlie21 2019-10-26 23:16:23 +08:00
它的相等和你的相等是一回事吗?你这个需求看起来实际上是想要自己定义何谓相等 ( “从逻辑上来说,a1 和 a2 的值是相等的”,这里你已经对你的 “相当” 有了明确的定义 )。那么你完全可以考虑自己定义 何谓 “相等” 并且直接去做。
|
13
yejinmo 2019-10-26 23:48:51 +08:00
|
14
geelaw 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) 里面的类型转换是多余的。 |
15
geelaw 2019-10-26 23:56:38 +08:00
关于你的第二个疑惑,一般来说我们会希望这俩不相等才对。例如一个人和一只宠物,它们都可以有名字和年龄,但是通常我们不希望它们有“相等”的概念。
|
16
geelaw 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) |
17
darktone 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)可用,不会产生异常。 |
19
geelaw 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)。 |