Object.GetHashCode 方法

定義

做為預設哈希函式。

public:
 virtual int GetHashCode();
public virtual int GetHashCode();
abstract member GetHashCode : unit -> int
override this.GetHashCode : unit -> int
Public Overridable Function GetHashCode () As Integer

傳回

目前物件的雜湊碼。

範例

計算數值哈希碼的最簡單方式之一,其範圍與 Int32 類型相同或更小的數值,就是只傳回該值。 下列範例示範 Number 的這類結構實作。

using System;

public struct Number
{
   private int n;

   public Number(int value)
   {
      n = value;
   }

   public int Value
   {
      get { return n; }
   }

   public override bool Equals(Object obj)
   {
      if (obj == null || ! (obj is Number))
         return false;
      else
         return n == ((Number) obj).n;
   }

   public override int GetHashCode()
   {
      return n;
   }

   public override string ToString()
   {
      return n.ToString();
   }
}

public class Example1
{
   public static void Main()
   {
      Random rnd = new Random();
      for (int ctr = 0; ctr <= 9; ctr++) {
         int randomN = rnd.Next(Int32.MinValue, Int32.MaxValue);
         Number n = new Number(randomN);
         Console.WriteLine("n = {0,12}, hash code = {1,12}", n, n.GetHashCode());
      }
   }
}
// The example displays output like the following:
//       n =   -634398368, hash code =   -634398368
//       n =   2136747730, hash code =   2136747730
//       n =  -1973417279, hash code =  -1973417279
//       n =   1101478715, hash code =   1101478715
//       n =   2078057429, hash code =   2078057429
//       n =   -334489950, hash code =   -334489950
//       n =    -68958230, hash code =    -68958230
//       n =   -379951485, hash code =   -379951485
//       n =    -31553685, hash code =    -31553685
//       n =   2105429592, hash code =   2105429592
open System

[<Struct; CustomEquality; NoComparison>]
type Number(value: int) =
    member _.Value = value

    override _.Equals(obj) =
        match obj with
        | :? Number as n ->
            n.Value = value
        | _ -> false

    override _.GetHashCode() =
        value

    override _.ToString() =
        string value

let rnd = Random()
for _ = 0 to 9 do
    let randomN = rnd.Next(Int32.MinValue, Int32.MaxValue)
    let n = Number randomN
    printfn $"n = {n,12}, hash code = {n.GetHashCode(),12}"
// The example displays output like the following:
//       n =   -634398368, hash code =   -634398368
//       n =   2136747730, hash code =   2136747730
//       n =  -1973417279, hash code =  -1973417279
//       n =   1101478715, hash code =   1101478715
//       n =   2078057429, hash code =   2078057429
//       n =   -334489950, hash code =   -334489950
//       n =    -68958230, hash code =    -68958230
//       n =   -379951485, hash code =   -379951485
//       n =    -31553685, hash code =    -31553685
//       n =   2105429592, hash code =   2105429592
Public Structure Number
   Private n As Integer

   Public Sub New(value As Integer)
      n = value
   End Sub

   Public ReadOnly Property Value As Integer
      Get
         Return n
      End Get
   End Property
   
   Public Overrides Function Equals(obj As Object) As Boolean
      If obj Is Nothing OrElse Not TypeOf obj Is Number Then
         Return False
      Else
         Return n = CType(obj, Number).n
      End If
   End Function      
   
   Public Overrides Function GetHashCode() As Integer
      Return n
   End Function
   
   Public Overrides Function ToString() As String
      Return n.ToString()
   End Function
End Structure

Module Example1
    Public Sub Main()
        Dim rnd As New Random()
        For ctr As Integer = 0 To 9
            Dim randomN As Integer = rnd.Next(Int32.MinValue, Int32.MaxValue)
            Dim n As New Number(randomN)
            Console.WriteLine("n = {0,12}, hash code = {1,12}", n, n.GetHashCode())
        Next
    End Sub
End Module
' The example displays output like the following:
'       n =   -634398368, hash code =   -634398368
'       n =   2136747730, hash code =   2136747730
'       n =  -1973417279, hash code =  -1973417279
'       n =   1101478715, hash code =   1101478715
'       n =   2078057429, hash code =   2078057429
'       n =   -334489950, hash code =   -334489950
'       n =    -68958230, hash code =    -68958230
'       n =   -379951485, hash code =   -379951485
'       n =    -31553685, hash code =    -31553685
'       n =   2105429592, hash code =   2105429592

通常,類型具有多個數據欄位,可參與產生哈希程式代碼。 產生雜湊代碼的一種方法是使用 XOR (eXclusive OR) 操作來結合這些欄位,如下列範例所示。

using System;

// A type that represents a 2-D point.
public struct Point2
{
    private int x;
    private int y;

    public Point2(int x, int y)
    {
       this.x = x;
       this.y = y;
    }

    public override bool Equals(Object obj)
    {
       if (! (obj is Point2)) return false;

       Point2 p = (Point2) obj;
       return x == p.x & y == p.y;
    }

    public override int GetHashCode()
    {
        return x ^ y;
    }
}

public class Example3
{
   public static void Main()
   {
      Point2 pt = new Point2(5, 8);
      Console.WriteLine(pt.GetHashCode());

      pt = new Point2(8, 5);
      Console.WriteLine(pt.GetHashCode());
   }
}
// The example displays the following output:
//       13
//       13
// A type that represents a 2-D point.
[<Struct; CustomEquality; NoComparison>]
type Point(x: int, y: int) =
    member _.X = x
    member _.Y = y

    override _.Equals(obj) =
        match obj with
        | :? Point as p ->
            x = p.X && y = p.Y
        | _ ->
            false

    override _.GetHashCode() =
        x ^^^ y

let pt = Point(5, 8)
printfn $"{pt.GetHashCode()}"

let pt2 = Point(8, 5)
printfn $"{pt2.GetHashCode()}"

// The example displays the following output:
//       13
//       13
' A type that represents a 2-D point.
Public Structure Point3
    Private x As Integer
    Private y As Integer

    Public Sub New(x As Integer, y As Integer)
        Me.x = x
        Me.y = y
    End Sub

    Public Overrides Function Equals(obj As Object) As Boolean
        If Not TypeOf obj Is Point3 Then Return False

        Dim p As Point3 = CType(obj, Point3)
        Return x = p.x And y = p.y
    End Function

    Public Overrides Function GetHashCode() As Integer
        Return x Xor y
    End Function
End Structure

Public Module Example3
    Public Sub Main()
        Dim pt As New Point3(5, 8)
        Console.WriteLine(pt.GetHashCode())

        pt = New Point3(8, 5)
        Console.WriteLine(pt.GetHashCode())
    End Sub
End Module

前一個範例回傳相同的雜湊碼(n1, n2)和(n2, n1),因此可能會產生比理想中更多的碰撞。 在 .NET 5+ 上,建議的解決方案是使用 HashCode.Combine。 它避免了對稱性問題,生成分布均勻的雜湊碼,且不會產生建立 Tuple 物件的開銷。

using System;

public struct Point3
{
    private int x;
    private int y;

    public Point3(int x, int y)
    {
       this.x = x;
       this.y = y;
    }

    public override bool Equals(Object obj)
    {
        if (obj is Point3)
        {
            Point3 p = (Point3) obj;
            return x == p.x & y == p.y;
        }
        else
        {
            return false;
        }      
    }

    public override int GetHashCode()
    {
        return HashCode.Combine(x, y);
    }
}

public class Example
{
   public static void Main()
   {
        Point3 pt = new Point3(5, 8);
        Console.WriteLine(pt.GetHashCode());

        pt = new Point3(8, 5);
        Console.WriteLine(pt.GetHashCode());
   }
}
// The example displays output similar to the following.
// Note: HashCode.Combine results are not stable across .NET versions.
//       185727722
//       -363254492
[<Struct; CustomEquality; NoComparison>]
type Point(x: int, y: int) =
    member _.X = x
    member _.Y = y

    override _.Equals(obj) =
        match obj with
        | :? Point as p ->
            x = p.X && y = p.Y
        | _ -> 
            false

    override _.GetHashCode() =
        System.HashCode.Combine(x, y)

let pt = Point(5, 8)
printfn $"{pt.GetHashCode()}"

let pt2 = Point(8, 5)
printfn $"{pt2.GetHashCode()}"
// The example displays output similar to the following.
// Note: HashCode.Combine results are not stable across .NET versions.
//       185727722
//       -363254492
Public Structure Point
    Private x As Integer
    Private y As Integer

    Public Sub New(x As Integer, y As Integer)
       Me.x = x
       Me.y = y
    End Sub
    
    Public Overrides Function Equals(obj As Object) As Boolean
       If Not TypeOf obj Is Point Then Return False
       
       Dim p As Point = CType(obj, Point)
       Return x = p.x And y = p.y
    End Function
    
    Public Overrides Function GetHashCode() As Integer 
        Return HashCode.Combine(x, y)
    End Function 
End Structure 

Public Module Example
    Public Sub Main() 
        Dim pt As New Point(5, 8)
        Console.WriteLine(pt.GetHashCode())
        
        pt = New Point(8, 5)
        Console.WriteLine(pt.GetHashCode())
    End Sub 
End Module         
' The example displays output similar to the following.
' Note: HashCode.Combine results are not stable across .NET versions.
'       185727722
'       -363254492

備註

方法 GetHashCode 會為需要快速檢查物件是否相等的演算法提供哈希碼。 哈希程式代碼是一個數值,用來在哈希型集合中插入和識別物件,例如 Dictionary<TKey,TValue> 類別、 Hashtable 類別或衍生自 類別的類型 DictionaryBase

Note

如需瞭解哈希碼在哈希表中的使用方式以及一些其他哈希代碼演算法,請參閱 Wikipedia 中的「Hash Function」條目。

相等的兩個 對象會傳回相等的哈希碼。 不過,反向不是真的:相等哈希碼並不表示物件相等,因為不同的 (不相等) 物件可以有相同的哈希碼。 此外,.NET 並不保證 GetHashCode 方法的預設實作,且此方法回傳的值可能因.NET實作與平台而異,例如在 32 位元與 64 位元平台間。 基於這些原因,請勿使用此方法的默認實作做為哈希用途的唯一對象標識碼。 下列兩個結果如下:

  • 您不應該假設相等哈希碼表示物件相等。
  • 您不應該在建立應用程式域外部保存或使用哈希程式代碼,因為相同的物件可能會跨應用程式域、進程和平台進行哈希。

Warning

哈希碼適用於以哈希表為基礎的集合中有效率的插入和查閱。 哈希碼不是永久值。 因此:

  • 請勿串行化哈希碼值,或將它們儲存在資料庫中。
  • 請勿使用哈希碼作為索引鍵,從索引鍵集合擷取物件。
  • 請勿跨應用程式域或行程傳送哈希碼。 在某些情況下,哈希碼可能會以個別進程或個別應用程式域為基礎來計算。
  • 如果您需要密碼編譯強式哈希,請勿使用哈希碼,而不是密碼編譯哈希函式所傳回的值。 針對密碼編譯哈希,請使用衍生自System.Security.Cryptography.HashAlgorithmSystem.Security.Cryptography.KeyedHashAlgorithm類別的類別。
  • 請勿測試哈希碼是否相等,以判斷兩個物件是否相等。 (不相等的物件可以有相同的哈希碼。若要測試是否相等,請呼叫 ReferenceEqualsEquals 方法。

GetHashCode方法可由衍生型別覆寫。 如果未 GetHashCode 覆寫,參考型別的哈希碼會藉由呼叫 Object.GetHashCode 基類的 方法來計算,其會根據對象的參考計算哈希碼;如需詳細資訊,請參閱 RuntimeHelpers.GetHashCode。 換句話說,方法傳ReferenceEquals回的兩個物件true具有相同的哈希碼。 如果值類型沒有覆寫 GetHashCode,則基類的 ValueType.GetHashCode 方法將使用反射,根據型別欄位的值來計算哈希碼。 換句話說,欄位具有相等值的實值型別具有相等的哈希碼。 如需覆寫 GetHashCode 的詳細資訊,請參閱「給繼承者的注意事項」一節。

Warning

如果您覆寫 GetHashCode 方法,則也應該覆寫 Equals,反之亦然。 如果當方法 Equals 覆寫之後,在測試兩個物件是否相等時回傳 true,那麼覆寫 GetHashCode 的方法必須也針對這兩個物件回傳相同的值。

如果作為哈希表中索引鍵的物件未提供有用的GetHashCode實作,您可以藉由提供IEqualityComparer實作給類別建構函式的其中一個多載Hashtable,來指定提供哈希代碼的提供者。

給繼承者的注意事項

雜湊函數用於快速產生一個數字(雜湊碼),對應於物件的值。 雜湊函數通常針對每種類型特定,且為了保持唯一性,必須至少使用其中一個實例欄位作為輸入。 雜湊碼不應利用靜態欄位的值來計算。

對於由 Object導出的 GetHashCode 類別,方法只有在導出類別定義等號為參考相等時,才能委派給基底類別 GetHashCode() 實作。 預設的 參考型別實作 GetHashCode() 會回傳一個與方法回傳 GetHashCode(Object) 的雜湊碼等價的雜湊碼。 你可以覆寫 GetHashCode() 不可變的參考類型。 一般而言,對於可變的參考類型,只有當以下情況才應該覆寫 GetHashCode()

  • 你可以從不可變的欄位計算雜湊碼;或

  • 你可以確保在物件包含於依賴其雜湊碼的集合中時,其雜湊碼不會改變。

否則,你可能會認為可變物件已經在雜湊表中遺失了。 如果你選擇覆蓋 GetHashCode() 可變參考型別,文件應該會明確說明,使用者在物件存放於雜湊表時,不應修改物件值。

對於值型別,提供 GetHashCode() 一個使用反射的預設雜湊碼實作。 你應該考慮覆蓋它以提升效能。

欲了解更多資訊及以多種方式計算雜湊碼的範例,請參閱範例章節。

雜湊函數必須具備以下特性:

  • 如果兩個物件比較為相等,則每個物件的方法 GetHashCode() 必須回傳相同的值。 然而,如果兩個物件的比較不等同, GetHashCode() 則兩個物件的方法不必回傳不同的值。

  • GetHashCode()只要物件狀態沒有變動,無法決定該物件 System.Object.Equals 方法的回傳值,物件的方法必須一致回傳相同的雜湊碼。 請注意,這僅適用於應用程式目前執行時,且若再次執行該應用程式,可能會回傳不同的雜湊碼。

  • 為了達到最佳效能,雜湊函數應該對所有輸入產生均勻分布,包括高度聚類的輸入。 其含意是,對物件狀態的微小修改應導致對雜湊碼進行大幅修改,以達到最佳雜湊表效能。

  • 雜湊函數的計算成本應該很低。

  • GetHashCode() 方法不應該拋出例外。

例如,該類別所提供的GetHashCode()方法實作String會回傳相同的雜湊碼,且字串值相同。 因此,若兩個 String 物件代表相同的字串值,則回傳相同的雜湊碼。 此外,該方法使用字串中的所有字元來產生合理隨機分布的輸出,即使輸入被聚集在特定範圍內(例如,許多使用者的字串可能只包含較低的 128 個 ASCII 字元,儘管一個字串可以包含 65,535 個 Unicode 字元中的任意一個)。

在類別上提供良好的雜湊函數,會大幅影響將這些物件加入雜湊表的效能。 在帶有鍵數提供良好雜湊函數實作的雜湊表中,搜尋元素需要常數時間(例如,O(1) 操作)。 在雜湊表中雜湊函數實作不佳,搜尋的效能取決於雜湊表中項目的數量(例如,O(n) 操作,其中 n 是雜湊表中的項目數量)。 惡意使用者可能會輸入資料,增加碰撞次數,進而大幅降低依賴雜湊表的應用程式效能,條件如下:

  • 當雜湊函數產生頻繁碰撞時。

  • 當雜湊表中大量物件產生相等或近似相等的雜湊碼時。

  • 當使用者輸入計算雜湊碼的資料時,

覆蓋 GetHashCode() 的衍生類別也必須 Equals(Object) 覆蓋,以確保兩個被視為相等的物件擁有相同的雜湊碼;否則,該 Hashtable 型別可能無法正常運作。

適用於

另請參閱