0

关于这个主题有一个很好的问题和答案: 我必须在新类中覆盖 GetHashCode 和 Equals 吗?

正如它所提到的:

如果您需要值相等语义,您只需要覆盖它们。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 

这样对吗?

  1. 需要注释的第一个 Equals 覆盖方法?
  2. 在这种情况下,我应该在 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 会发生什么?

4

2 回答 2

2

需要注释的第一个 Equals 覆盖方法?

一些比较使用通用版本,一些使用非通用版本。如果您已经拥有通用版本,那么这是一个相当简单的实现,因此实现它并没有什么坏处。

在这种情况下,我应该在 GetHashCode 覆盖中包含哪些类成员?参与 Equals 方法的所有成员?

唯一的要求GetHashCode两个“相等”的对象必须返回相同的哈希码(反之则不然——两个相等的哈希码并不意味着相等的对象)。

因此,您GetHashCode可以做任何事情,从返回一个常量(平底船并用于Equals确定相等性)到一个返回尽可能多的不同哈希码的精细函数。

为了在使用基于散列的集合时获得合理的性能,您应该设计GetHashCode逻辑以最大限度地减少冲突。这通常是通过迭代地将哈希码乘以质数来完成的。

另一个关键是哈希码不能改变,这意味着派生哈希码的值不能改变。如果哈希码在对象的整个生命周期中发生了变化,则无法在字典中找到该项目,因为它根据其哈希值存储项目。

如果你想基于一个可以改变的值来定义“平等”,你最好在一个单独的实现的类中这样做,IEqualityComparer但需要注意的是,如果要使用对象进行哈希处理,则不应修改对象-基于查找。

如果我只使用FirstName例如会发生什么?

与使用所有相关字段相比,您可能会遇到更多的冲突,这仅意味着系统在基于散列的集合中查找项目时必须做更多的工作。它首先找到所有具有计算哈希值的对象,然后使用 . 检查原始对象Equals

请注意,在您的实现中,您应该对 、 和 进行空FirstNameLastName检查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());
于 2015-02-09T16:59:10.790 回答
0

这将取决于您打算如何比较您的用户。如果您希望相等比较仅在比较对同一用户对象的两个引用时返回 true,则不需要您的 equals 覆盖。

但是,如果您想根据其他逻辑比较用户,那么您应该在 equals 和 GetHashCode 实现中使用哪些字段的答案取决于您的特定上下文。在进行相等比较时,如果两个用户的名字和姓氏相同,您会认为他们相等吗?如果他们的名字和姓氏相同但地址不同怎么办?您认为定义唯一用户的任何字段都是您应该使用的字段。

于 2015-02-09T16:34:17.017 回答