【C#】什么时候需要重写 Equals() 方法 & 如何重写 Equals() 方法
参考资料:How to: define value equality for a type (C# Programming Guide)
什么时候需要重写 Equals()
方法
引用类型:
只有当需要修改该引用类型所定义的语义时,才应该重写实例版本的 Equals() 方法。如果类型需要采用值语义而不是引用语义(或者说,需要按照对象内容而不是对象身份来进行比较),那么就应该针对这个类型重写实例版本的 Object.Equals() 方法。
引用类型一般不需要重写 operator==()。
值类型:
创建值类型的时候,总是应该针对这个类型重写 ValueType.Equals()
方法。
因为值类型都继承自 System.ValueType
类,System.ValueType
类默认通过反射来实现比较,效率不够高。
值类型中默认的 ==
运算符会默认通过反射进行比较,因此,也应该重写 ==
操作符。
重写 Equals()
方法时的注意事项
Equals()
方法必须满足等同关系的 3 项数学性质:自反性、对称性、可传递性。Equals()
方法决不应该抛出异常。- 重写
Equals()
方法时,只有在基类型的Equals(object)
不是由System.Object
或System.ValueType
所提供的情况下,才需要调用基类型的版本。 - 重写
Equals()
的时候,还应该让该类型实现IEquatable<T>
接口。 - 重写
Equals()
方法后,通常应该同时重写GetHashCode()
方法。
重写 GetHashCode()
方法时的注意事项
- 如果
Equals()
方法认定两个对象相等,那么这两个对象的HashCode
也必须相同; - 对任意对象来说,其
HashCode
必须在生命周期内保持不变; HashCode
计算方法应该将其值均匀地映射到各个整数上,避免堆集。
一种常用的 HashCode
算法是:对类型中的每个相互独立的不可变字段调用 GetHashCode()
方法,并对返回的 HashCode
进行异或(XOR
)运算,将得到的最终结果作为对象本身的 HashCode
。
示例代码
using System;
// 引用类型示例
public class ReferenceType : IEquatable<ReferenceType>
{
public override bool Equals(object obj)
{
//return base.Equals(obj);
return this.Equals(obj as ReferenceType);
}
public bool Equals(ReferenceType other)
{
if (object.ReferenceEquals(other, null))
return false;
if (object.ReferenceEquals(this, other))
return true;
if (this.GetType() != other.GetType())
return false;
// 根据具体的判断标准进行判断,此处省略
throw new NotImplementedException();
}
}
// 值类型示例
public struct ValueType : IEquatable<ValueType>
{
public override bool Equals(object obj)
{
//return base.Equals(obj);
if (obj is ValueType other)
{
return this.Equals(other);
}
return false;
}
public bool Equals(ValueType other)
{
// 根据具体的判断标准进行判断(例如比较每个成员值),此处省略
throw new NotImplementedException();
}
public static bool operator ==(ValueType left, ValueType right)
{
return left.Equals(right);
}
public static bool operator !=(ValueType left, ValueType right)
{
return !(left.Equals(right));
}
}