对于类型“T”的相等比较,重载这些方法:
int GetHashCode() //Overrides Object.GetHashCode
bool Equals(object other) //Overrides Object.Equals; would correspond to IEquatable, if such an interface existed
bool Equals(T other) //Implements IEquatable<T>; do this for each T you want to compare to
static bool operator ==(T x, T y)
static bool operator !=(T x, T y)
您的特定类型比较代码应该在一个地方完成:类型安全的IEquatable<T>
接口方法Equals(T other)
。如果您要与另一种类型 (T2) 进行比较,请同时实现IEquatable<T2>
,并将该类型的字段比较代码放入 Equals(T2 other) 中。
所有重载的方法和运算符都应将相等比较任务转发给主要类型安全的 Equals(T other) 实例方法,以便维护干净的依赖层次结构,并在每个级别引入更严格的保证,以消除冗余和不必要的复杂性。
bool Equals(object other)
{
if (other is T) //replicate this for each IEquatable<T2>, IEquatable<T3>, etc. you may implement
return Equals( (T)other) ); //forward to IEquatable<T> implementation
return false; //other is null or cannot be compared to this instance; therefore it is not equal
}
bool Equals(T other)
{
if ((object)other == null) //cast to object for reference equality comparison, or use object.ReferenceEquals
return false;
//if ((object)other == this) //possible performance boost, ONLY if object instance is frequently compared to itself! otherwise it's just an extra useless check
//return true;
return field1.Equals( other.field1 ) &&
field2.Equals( other.field2 ); //compare type fields to determine equality
}
public static bool operator ==( T x, T y )
{
if ((object)x != null) //cast to object for reference equality comparison, or use object.ReferenceEquals
return x.Equals( y ); //forward to type-safe Equals on non-null instance x
if ((object)y != null)
return false; //x was null, y is not null
return true; //both null
}
public static bool operator !=( T x, T y )
{
if ((object)x != null)
return !x.Equals( y ); //forward to type-safe Equals on non-null instance x
if ((object)y != null)
return true; //x was null, y is not null
return false; //both null
}
讨论:
前面的实现将类型特定(即字段相等)的比较集中到该类型的IEquatable<T>
实现的末尾。和运算==
符!=
具有并行但相反的实现。我更喜欢这个而不是一个引用另一个,这样就有一个额外的方法调用依赖的一个。如果!=
操作员只是要调用==
操作员,而不是提供同等性能的操作员,那么您最好只使用!(obj1 == obj2)
并避免额外的方法调用。自我比较从等于运算符和IEquatable<T>
实现中省略,因为它可能会引入 1. 在某些情况下不必要的开销,和/或 2. 不一致的性能取决于实例与自身与其他实例的比较频率。
我不喜欢但应该提到的另一种方法是反转此设置,将特定于类型的相等代码集中在相等运算符中,并让 Equals 方法依赖于此。然后可以使用快捷方式ReferenceEquals(obj1,obj2)
正如 Philip 在之前的一篇文章中提到的那样,同时检查引用相等和空相等,但这种想法具有误导性。似乎你用一块石头杀死了两只鸟,但实际上你创造了更多的工作——在确定对象既不是空的也不是同一个实例之后,你还必须继续检查每个实例是否一片空白。在我的实现中,您检查任何单个实例是否为 null 一次。在调用 Equals 实例方法时,已经排除了第一个被比较的对象为空,所以剩下要做的就是检查另一个是否为空。所以最多两次比较后,我们直接跳到字段检查中,无论我们使用哪种方法(Equals(object),Equals(T),==,!=
)。另外,正如我所提到的,如果您确实在大多数时间都在比较并反对自己,那么您可以在深入进行现场比较之前在 Equals 方法中添加该检查。最后添加它的要点是,您仍然可以维护流/依赖关系层次结构,而无需在每个级别引入冗余/无用检查。