C#,覆盖GetHashCode和Equals时应该考虑哪个class fields/members?
C#, Which class fields/members should be considered when overriding GetHashCode and Equals?
关于这个话题有一个很好的问答:
Do I HAVE to override GetHashCode and Equals in new Classes?
正如它提到的:
you only need to override them if you need value equality semantics. The System.Object implementation isn't 'bad', it just only does a reference check (which is all an implementation at that level can do).
In short: If you need some sort of value based equality (equality based on properties of the class), then yes, override away. Otherwise, it should be more than fine already.
假设我有一个 class 用户:
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
对吗?
- 需要注释的第一个 Equals 覆盖方法吗?
在这种情况下,我应该在 GetHashCode 覆盖中包含哪些 class 成员? 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 会怎样?
这取决于您打算如何比较用户。如果仅在比较对同一用户对象的两个引用时才希望与 return true 进行相等比较,则不需要 equals 覆盖。
但是,如果您想根据其他逻辑比较用户,那么您应该在 equals 和 GetHashCode 实现中使用哪些字段取决于您的特定上下文。在进行相等比较时,如果两个用户的名字和姓氏相同,您会认为他们相等吗?如果他们的名字和姓氏相同但地址不同怎么办?无论您认为定义唯一用户的字段都是您应该使用的字段。
The first Equals override method commented is required?
一些比较使用通用版本,一些使用非通用版本。由于这是一个相当简单的实现,如果您已经拥有通用版本,那么实现它没有任何害处。
In this case, which class members should I include in the GetHashCode override? All the members involved in the Equals method?
GetHashCode
的唯一 要求 是 "equal" 的两个对象必须 return 相同的哈希码(反之则不是true - 两个相等的哈希码并不意味着相等的对象)。
因此您的 GetHashCode
可以做任何事情,从 return 一个常量(平底船并使用 Equals
确定相等性)到一个复杂的函数 return尽可能不同的哈希码。
为了在使用基于哈希的集合时获得合理的性能,您应该设计GetHashCode
逻辑以尽量减少冲突。这通常是通过迭代地将哈希码乘以素数来完成的。
另一个关键是哈希码不能改变,这意味着导出哈希码的值不能改变。如果哈希码在对象的生命周期内发生变化,则不可能在字典中找到该项目,因为它根据哈希值存储项目。
如果您想根据可以更改的值定义 "equality",您最好在实现 IEqualityComparer
的单独 class 中执行此操作,但需要注意的是如果要将对象用于进行基于散列的查找,则不应修改这些对象。
What happens if I only use FirstName
for example?
与使用所有相关字段相比,您可能会遇到更多的冲突,这仅意味着在基于散列的集合中查找项目时,系统必须做更多的工作。它首先找到所有具有计算哈希值的对象,然后使用 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());
关于这个话题有一个很好的问答: Do I HAVE to override GetHashCode and Equals in new Classes?
正如它提到的:
you only need to override them if you need value equality semantics. The System.Object implementation isn't 'bad', it just only does a reference check (which is all an implementation at that level can do).
In short: If you need some sort of value based equality (equality based on properties of the class), then yes, override away. Otherwise, it should be more than fine already.
假设我有一个 class 用户:
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
对吗?
- 需要注释的第一个 Equals 覆盖方法吗?
在这种情况下,我应该在 GetHashCode 覆盖中包含哪些 class 成员? 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 会怎样?
这取决于您打算如何比较用户。如果仅在比较对同一用户对象的两个引用时才希望与 return true 进行相等比较,则不需要 equals 覆盖。
但是,如果您想根据其他逻辑比较用户,那么您应该在 equals 和 GetHashCode 实现中使用哪些字段取决于您的特定上下文。在进行相等比较时,如果两个用户的名字和姓氏相同,您会认为他们相等吗?如果他们的名字和姓氏相同但地址不同怎么办?无论您认为定义唯一用户的字段都是您应该使用的字段。
The first Equals override method commented is required?
一些比较使用通用版本,一些使用非通用版本。由于这是一个相当简单的实现,如果您已经拥有通用版本,那么实现它没有任何害处。
In this case, which class members should I include in the GetHashCode override? All the members involved in the Equals method?
GetHashCode
的唯一 要求 是 "equal" 的两个对象必须 return 相同的哈希码(反之则不是true - 两个相等的哈希码并不意味着相等的对象)。
因此您的 GetHashCode
可以做任何事情,从 return 一个常量(平底船并使用 Equals
确定相等性)到一个复杂的函数 return尽可能不同的哈希码。
为了在使用基于哈希的集合时获得合理的性能,您应该设计GetHashCode
逻辑以尽量减少冲突。这通常是通过迭代地将哈希码乘以素数来完成的。
另一个关键是哈希码不能改变,这意味着导出哈希码的值不能改变。如果哈希码在对象的生命周期内发生变化,则不可能在字典中找到该项目,因为它根据哈希值存储项目。
如果您想根据可以更改的值定义 "equality",您最好在实现 IEqualityComparer
的单独 class 中执行此操作,但需要注意的是如果要将对象用于进行基于散列的查找,则不应修改这些对象。
What happens if I only use
FirstName
for example?
与使用所有相关字段相比,您可能会遇到更多的冲突,这仅意味着在基于散列的集合中查找项目时,系统必须做更多的工作。它首先找到所有具有计算哈希值的对象,然后使用 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());