簡體   English   中英

如何識別GetHashCode的錯誤實現?

[英]How can I identify a bad implementation of GetHashCode?

我有一個GetHashCode的實現,我相信它是相當健壯的,但是說實話,我是從Internet的深處挖出來的,盡管我理解了所寫的內容,但我不認為有資格將其描述為“好” ”或GetHashCode的“錯誤”實現。

我在StackOverflow上做了很多有關GetHashCode的閱讀。 是否有示例為什么應在NHibernate中覆蓋Equals / GetHashCode? 我認為該線程可能是最好的信息來源,但仍然讓我感到疑惑。

考慮以下實體及其Equals和GetHashCode的給定實現:

public class Playlist : IAbstractDomainEntity
{
    public Guid Id { get; set; }
    public string Title { get; set; 
    public Stream Stream { get; set; }
    //  Use interfaces so NHibernate can inject with its own collection implementation.
    public IList<PlaylistItem> Items { get; set; }
    public PlaylistItem FirstItem { get; set; }
    public Playlist NextPlaylist { get; set; }
    public Playlist PreviousPlaylist { get; set; }

    private int? _oldHashCode;
    public override int GetHashCode()
    {
        // Once we have a hash code we'll never change it
        if (_oldHashCode.HasValue)
            return _oldHashCode.Value;

        bool thisIsTransient = Equals(Id, Guid.Empty);

        // When this instance is transient, we use the base GetHashCode()
        // and remember it, so an instance can NEVER change its hash code.
        if (thisIsTransient)
        {
            _oldHashCode = base.GetHashCode();
            return _oldHashCode.Value;
        }
        return Id.GetHashCode();
    }

    public override bool Equals(object obj)
    {
        Playlist other = obj as Playlist;
        if (other == null)
            return false;

        // handle the case of comparing two NEW objects
        bool otherIsTransient = Equals(other.Id, Guid.Empty);
        bool thisIsTransient = Equals(Id, Guid.Empty);
        if (otherIsTransient && thisIsTransient)
            return ReferenceEquals(other, this);

        return other.Id.Equals(Id);
    }
}

在此實現中吹捧的安全檢查量似乎是最高的。 它激發了我的信心-假設編寫此書的人比我理解的更多情況-但也讓我想知道為什么我看到這么多簡單的實現。

當覆蓋Equals方法時,覆蓋GetHashCode為什么很重要? 查看所有這些不同的實現。 下面是一個簡單但評級很高的實現:

  public override int GetHashCode()
  {
        return string.Format("{0}_{1}_{2}", prop1, prop2, prop3).GetHashCode();
  }

這個實現會比我提供的實現好還是壞? 為什么?

兩者同等有效嗎? 實現GetHashCode時是否應遵循標准的“指南”? 上面的實現有明顯的缺陷嗎? 一個人如何創建測試用例來驗證GetHashCode的實現?

GetHashCode應該與您的類/環境的“等於”概念匹配(除了在容器中保持不變且快速)。

在正常情況下,“等於”是比較對應對象的所有字段(值類型比較)。 在這種情況下,以某種方式合並所有字段的哈希碼的簡單實現就足夠了。

我的理解是,在NHibernate的情況下,“相等”要棘手得多,結果您會看到復雜的實現。我相信這主要是由於某些對象屬性可能尚不可用的事實-在這種情況下,比較對象的“身份”是足夠。

不幸的是,盡管相等測試方法可以有意義地詢問任何一對對象引用XY兩個不同問題,但只有一個Equals方法和一個GetHashCode方法。

  • 假設XY具有相同的類型(*), X所有成員是否總是表現出與Y相應方法相同的行為? 即使兩個引用包含匹配的元素,在此定義下,對不同數組的兩個引用也將報告為不相等,因為即使它們的元素在某一時刻相同,也不一定總是正確的。

  • 假設XY的類型相同(*),是否同時將對對象X所有引用替換為對對象Y引用,反之亦然,是否會影響除基於身份的GetHashCode函數以外的任何其他成員? 在此定義下,對元素匹配的兩個不同數組的引用將報告為相等。

(*)通常,不同類型的對象應報告不相等。 在某些情況下,如果可以訪問私有類的所有代碼僅以匹配的公共類型存儲引用,則可能有人認為從同一公共類繼承的不同私有類的對象應被視為相等。那最多是一個非常狹窄的例外。

有些情況需要詢問第一個問題,有些情況需要詢問第二個問題; EqualsGetHashCode的默認Object實現回答第一個,而默認的ValueType實現回答第二個。 不幸的是,選擇哪種比較方法適合給定的引用取決於引用的使用方式,而不是引用實例類型的函數。 如果兩個對象持有對集合的引用,而它們既不會突變也不會暴露於可能這樣做的代碼,則為了封裝其內容,持有這些引用的對象的相等性應取決於集合的內容,而不是其標識。

看起來代碼有時以第一個問題更合適的方式使用PlayList類型的實例,有時以第二個問題更合適的方式使用。 雖然這可能是可行的,但我認為最好有一個通用的數據持有者對象,如果需要,可以將其包裝為一個對象,該對象的相等性檢查方法將適合一種用途或另一種用途(例如,具有一個PlaylistData對象,可以由MutablePlaylistImmutablePlaylist包裹)。 包裝器類可以具有InvalidateAndMakeImmutableInvalidateAndMakeMutable方法,這些方法將使包裝器無效並返回圍繞對象的新包裝器(使用包裝器將確保系統將知道給定的Playlist引用是否可能暴露於可能對其進行變異的代碼)。

暫無
暫無

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

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