关于这个主题有这个极好的问题和答案: Do I HAVE to override GetHashCode and Equals in new Classes?
正如它提到的那样:
如果需要值相等语义,则只需要覆盖它们。 System.Object实现并不是坏事,它只是进行引用检查(这是该级别的所有实现都可以)。
简而言之:如果你需要某种基于值的相等(基于类的属性的相等),那么是,覆盖掉。否则,它应该已经很好了。
假设我有一个用户类:
public class User: IEquatable<User>
{
private readonly string _firstName;
private readonly string _lastName;
private readonly string _address;
public User (string firstName, string lastName, string address)
{
this._firstName = firstName;
this._lastName = lastName;
this._address = address;
}
public FirstName {get; private set;}
public LastName {get; private set;}
public Address {get; private set;}
//should I need to override this?
public override bool Equals(object right)
{
if (object.ReferenceEquals(right, null))
return false;
if (object.ReferenceEquals(this, right))
return true;
if (this.GetType() != right.GetType())
return false;
return this.Equals(right as User);
}
#region IEquatable<User> Members
public bool Equals(User user)
{
bool isEqual = (this._firstName != null && this._firstName.Equals(user.FirstName, StringComparison.InvariantCultureIgnoreCase)) ||
(this._lastName != null && this._lastName.Equals(user.LastName, StringComparison.InvariantCultureIgnoreCase)) ||
(this._address != null && this._address.Equals(user.Address, StringComparison.InvariantCultureIgnoreCase)) ||
(this._firstName == null && this._lastName == null && this._address == null);
return isEqual;
}
#endregion
}
User user1 = new User("John", "Wayne", "Collins Avenue");
User user2 = new User("John", "Wayne", "Collins Avenue");
//if I don't override these methods, reference equals will be:
user1 == user2 // false
//if I override GetHashCode and Equals methods, then:
user1 == user2 //true
IList<User> usersList1 = new List<User>();
IList<User> usersList2 = new List<User>();
usersList1.Add(user1);
usersList2.Add(user2);
IList<User> finalUsersList = usersList1.Union(usersList2);
//if I don't override these methods, count will be:
finalUsersList.Count() // 2
//if I do override these methods, count will be:
finalUsersList.Count() // 1
是不是?
在这种情况下,我应该在GetHashCode覆盖中包含哪些类成员?参与Equals方法的所有成员?
public override int GetHashCode()
{
unchecked
{
// Hash -> primes
int hash = 17;
hash = hash * 23 + FirstName.GetHashCode();
hash = hash * 23 + LastName.GetHashCode();
hash = hash * 23 + Address.GetHashCode();
return hash;
}
}
如果我只使用FirstName会怎么样?
答案 0 :(得分:1)
是否需要注释第一个Equals覆盖方法?
某些比较使用通用版本,有些使用非通用版本。如果你已经拥有通用版本,这是一个相当简单的实现,那么实现它是没有害处的。
在这种情况下,我应该在GetHashCode覆盖中包含哪些类成员?参与Equals方法的所有成员?
GetHashCode
的唯一要求是两个“相等”的对象必须返回相同的哈希码(反之亦然) - 两个相等的哈希码并不意味着相等的对象)。
所以你的GetHashCode
可以做任何事情,从返回一个常量(punt并使用Equals
来确定相等)到一个精心设计的函数,它返回尽可能多的不同哈希码。
对于使用基于散列的集合时的合理性能,您应该设计GetHashCode
逻辑以最小化冲突。这通常通过在执行时将哈希码迭代乘以素数来完成。
另一个关键是哈希码无法更改,这意味着派生哈希码的值不能更改。如果哈希代码在对象的生命周期中发生了变化,则无法在字典中找到该项,因为它根据哈希值存储项目。
如果你想根据一个可以改变的值来定义“相等”,你可以更好地在一个实现IEqualityComparer
的单独的类中做到这一点,但需要注意的是,如果这些对象不应被修改将用于执行基于散列的查找。
如果我仅使用
FirstName
会怎么样?
与使用所有相关字段相比,您可能会遇到更多冲突,这意味着系统在查找基于散列的集合中的项目时必须执行更多工作。它首先查找具有计算哈希的所有对象,然后使用Equals
检查原始对象。
请注意,在您的实现中,您应该对FirstName
,LastName
和Address
执行空值检查:
hash = hash * 23 + (FirstName == null ? 0 : FirstName.GetHashCode());
hash = hash * 23 + (LastName == null ? 0 : LastName.GetHashCode());
hash = hash * 23 + (Address == null ? 0 : Address.GetHashCode());
答案 1 :(得分:0)
这取决于您打算如何比较用户。如果您希望相等比较仅在比较对同一用户对象的两个引用时返回true,则不需要等于覆盖。
但是,如果您想基于其他逻辑比较用户,那么您应该在equals和GetHashCode实现中使用哪些字段的答案取决于您的特定上下文。在进行相等比较时,如果两个用户具有相同的名字和姓氏,您会认为两个用户相等吗?如果他们有相同的名字和姓氏但不是相同的地址呢?无论您认为哪个字段都定义了一个独特的用户,您应该使用它们。