自己尝试写出如下代码,来判断对象相等,虽然能正常工作
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),运行结果是两个数组相等。