簡體   English   中英

為這個結構體實現 GetHashCode 的正確方法

[英]Right way to implement GetHashCode for this struct

我想使用日期范圍(從一個日期到另一個日期)作為字典的鍵,所以我編寫了自己的結構:

   struct DateRange
   {
      public DateTime Start;
      public DateTime End;

      public DateRange(DateTime start, DateTime end)
      {
         Start = start.Date;
         End = end.Date;
      }

      public override int GetHashCode()
      {
         // ???
      }
   }

實現 GetHashCode 以便不同范圍的兩個對象不會生成相同的哈希值的最佳方法是什么? 我希望哈希沖突盡可能不可能發生,盡管我知道 Dictionary<> 仍然會檢查我也將實現的相等運算符,但不想過多地污染示例代碼。 謝謝!

您可以使用 Effective Java 中的方法,正如 Jon Skeet 在此處展示的那樣。 對於您的特定類型:

public override int GetHashCode()
{
    unchecked // Overflow is fine, just wrap
    {
        int hash = 17;
        hash = hash * 23 + Start.GetHashCode();
        hash = hash * 23 + End.GetHashCode();
        return hash;
    }
}

C# 7你可以這樣做:

public override int GetHashCode() => (Start, End).GetHashCode();

ValueTuple.NET Framework 4.7.NET Core可用,或通過NuGet 提供

不確定它的性能如何,但如果任何自定義代碼能打敗它,我會感到驚訝。

我會相信微軟在元組中實現 GetHashCode() 並使用這樣的東西,沒有任何愚蠢的魔法:

public override int GetHashCode()
{
    Tuple.Create(x, y).GetHashCode();
}

不是為了讓死者復活,但我來這里是為了尋找一些東西,對於較新的 C# 版本,您可以這樣做

public override int GetHashCode()
{
    return HashCode.Combine(Start, End);
}

源目前可以在這里找到: https : //github.com/dotnet/corert/blob/master/src/System.Private.CoreLib/shared/System/HashCode.cs

在我的初步測試中(使用 Jon Skeets 微基准測試框架),就性能而言,它似乎與公認的答案非常相似,如果不一樣的話。

由於 DateTime.GetHashCode 是內部基於 Ticks 的,那么這個呢:

    public override int GetHashCode()
    {
        return unchecked((int)(Start.Ticks ^ End.Ticks));
    }

或者,由於您似乎對日期部分(年、月、日)而不是全部感興趣,因此此實現使用兩個日期之間的天數,並且幾乎不會產生沖突:

        public override int GetHashCode()
        {
            return unchecked((int)Start.Date.Year * 366 + Start.Date.DayOfYear + (End.Date - Start.Date).Days);
        }

就像它可能會幫助將來使用 Visual Studio Pro 的人一樣(不確定社區版中是否也存在這種情況)

  • 選擇所需的屬性(在您的情況下全部)
  • 按重構(CTRL + . 或右鍵單擊“快速操作和重構”)
  • 現在您可以選擇實現 Equals 或 GetHashcode(並且可能總是采用最知名的 MS 方式來實現)

像這樣:) 具有不同的質數:)

public override int GetHashCode()
{
    unchecked  
    {
        int hash = 23;
        // Suitable nullity checks etc, of course :)
        hash = hash * 31 + Start.GetHashCode();
        hash = hash * 31 + End.GetHashCode();
        return hash;
    }
}

這不是最快的實現,但它產生了一個很好的哈希碼。 Joshua bloch 也指出,並且您還計算了性能,^ 通常更快。 如果我錯了糾正我。

參見 Jon Skeets impl for c# :

結合 Jon Skeet 的回答和對該問題的評論(所以請不要對此投票,只是鞏固):

struct DateRange
{
    private readonly DateTime start;

    private readonly DateTime end;

    public DateRange(DateTime start, DateTime end)
    {
        this.start = start.Date;
        this.end = end.Date;
    }

    public DateTime Start
    {
        get
        {
            return this.start;
        }
    }

    public DateTime End
    {
        get
        {
            return this.end;
        }
    }

    public static bool operator ==(DateRange dateRange1, DateRange dateRange2)
    {
        return dateRange1.Equals(dateRange2);
    }

    public static bool operator !=(DateRange dateRange1, DateRange dateRange2)
    {
        return !dateRange1.Equals(dateRange2);
    }

    public override int GetHashCode()
    {
        // Overflow is fine, just wrap
        unchecked
        {
            var hash = 17;

            // Suitable nullity checks etc, of course :)
            hash = (23 * hash) + this.start.GetHashCode();
            hash = (23 * hash) + this.end.GetHashCode();
            return hash;
        }
    }

    public override bool Equals(object obj)
    {
        return (obj is DateRange)
            && this.start.Equals(((DateRange)obj).Start)
            && this.end.Equals(((DateRange)obj).End);
    }
}

暫無
暫無

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

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