簡體   English   中英

C#,在重寫GetHashCode和Equals時應考慮哪些類字段/成員?

[英]C#, Which class fields/members should be considered when overriding GetHashCode and Equals?

關於此主題有一個出色的問答: 我是否必須重寫新類中的GetHashCode和Equals?

正如它提到的:

僅在需要值相等語義時才需要覆蓋它們。 System.Object實現不是“不好的”,它僅執行引用檢查(該級別的所有實現都可以做到)。

簡而言之:如果您需要某種基於值的相等性(基於類的屬性的相等性),則可以,請覆蓋掉。 否則,應該已經足夠了。

假設我有一個User類:

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重寫中包括哪些類成員? 所有參與平等方法的成員?

     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; } } 

例如,如果我僅使用名字,會發生什么?

需要注釋的第一個Equals覆蓋方法嗎?

有些比較使用通用版本,有些則使用非通用版本。 由於這是一個相當瑣碎的實現,如果您已經擁有通用版本,則實現它不會有任何危害。

在這種情況下,應該在GetHashCode重寫中包括哪些類成員? 所有參與平等方法的成員?

GetHashCode的唯一要求是,兩個“相等”的對象必須返回相同的哈希碼(反之則不成立-兩個相等的哈希碼並不意味着相等的對象)。

因此,您的GetHashCode可以執行任何操作,從返回常量(平移並使用Equals確定相等性)到復雜的函數,該函數將返回盡可能多的不同哈希碼。

為了在使用基於哈希的集合時獲得合理的性能 ,應設計GetHashCode邏輯以最大程度地減少沖突。 通常,這是通過在執行操作時將哈希碼乘以質數來迭代完成的。

另一個關鍵是哈希碼不能更改 ,這意味着派生哈希碼的值不能更改。 如果哈希碼在對象的生命周期內發生了變化,則不可能在字典中找到該項目,因為它會基於其哈希值存儲項目。

如果要基於可以更改的值定義“平等”,則最好在實現IEqualityComparer的單獨類中進行此操作,並告誡不要修改對象(如果要使用它們進行哈希處理)基於的查找。

例如,如果我僅使用FirstName ,會發生什么?

與使用所有相關字段相比,您可能會遇到更多的沖突,這僅意味着在基於散列的集合中查找項目時,系統必須做更多的工作。 它首先查找具有計算出的哈希值的所有對象,然后使用Equals對照原始對象進行檢查。

請注意,在實現中,您應該對FirstNameLastNameAddress進行空檢查:

    hash = hash * 23 + (FirstName == null ? 0 : FirstName.GetHashCode());
    hash = hash * 23 + (LastName  == null ? 0 : LastName.GetHashCode());
    hash = hash * 23 + (Address   == null ? 0 : Address.GetHashCode());

這將取決於您打算如何比較用戶。 如果希望相等比較僅在將兩個引用與同一個用戶對象進行比較時返回true,則不需要equals覆蓋。

但是,如果要基於其他邏輯比較用戶,則關於在equals和GetHashCode實現中應使用哪些字段的答案取決於您的特定上下文。 在進行相等比較時,如果兩個用戶的名字和姓氏相同,您會認為兩個用戶相等嗎? 如果他們的名字和姓氏相同但地址不同怎么辦? 您認為應該使用哪個字段定義唯一用戶。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM