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

目前物件的雜湊碼。

範例

計算數值雜湊碼的其中一個最簡單方法,其範圍 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 Example
{
   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 Example
   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 Point
{
    private int x;
    private int y;

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

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

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

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

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

      pt = new Point(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 $"{pt.GetHashCode()}"
// The example displays the following output:
//       13
//       13
' A type that represents a 2-D point.
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 x Xor 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

上述範例 (會針對 n1、n2) 和 (n2、n1) 傳回相同的雜湊碼,因此可能會產生比所需的更多衝突。 有數個解決方案可供使用,因此在這些情況下的雜湊碼不相同。 其中一 Tuple 個是傳回物件的雜湊碼,以反映每個欄位的順序。 下列範例示範使用 類別的可能實作 Tuple<T1,T2> 。 不過請注意,具現化 Tuple 物件的效能額外負荷可能會大幅影響將大量物件儲存在雜湊表中之應用程式的整體效能。

using System;

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

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

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

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

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

        pt = new Point(8, 5);
        Console.WriteLine(pt.GetHashCode());
   }
}
// The example displays the following output:
//       173
//       269
[<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).GetHashCode()

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

let pt2 = Point(8, 5)
printfn $"{pt2.GetHashCode()}"
// The example displays the following output:
//       173
//       269
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 Tuple.Create(x, y).GetHashCode()
    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 the following output:
'       173
'       269

第二個替代解決方案牽涉到將連續欄位的雜湊碼向左移移兩個以上的位,以加權個別雜湊碼。 最佳方式是,位移超過位 31 的位應該換行,而不是捨棄。 由於 C# 和 Visual Basic 中的左移運算子會捨棄位,因此這需要建立左移和換行方法,如下所示:

public int ShiftAndWrap(int value, int positions)
{
    positions = positions & 0x1F;

    // Save the existing bit pattern, but interpret it as an unsigned integer.
    uint number = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0);
    // Preserve the bits to be discarded.
    uint wrapped = number >> (32 - positions);
    // Shift and wrap the discarded bits.
    return BitConverter.ToInt32(BitConverter.GetBytes((number << positions) | wrapped), 0);
}
let shiftAndWrap (value: int) positions =
    let positions = positions &&& 0x1F

    // Save the existing bit pattern, but interpret it as an unsigned integer.
    let number = BitConverter.ToUInt32(BitConverter.GetBytes value, 0)
    // Preserve the bits to be discarded.
    let wrapped = number >>> (32 - positions)
    // Shift and wrap the discarded bits.
    BitConverter.ToInt32(BitConverter.GetBytes((number <<< positions) ||| wrapped), 0)
Public Function ShiftAndWrap(value As Integer, positions As Integer) As Integer
   positions = positions And &h1F
   
   ' Save the existing bit pattern, but interpret it as an unsigned integer.
   Dim number As UInteger = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0)
   ' Preserve the bits to be discarded.
   Dim wrapped AS UInteger = number >> (32 - positions)
   ' Shift and wrap the discarded bits.
   Return BitConverter.ToInt32(BitConverter.GetBytes((number << positions) Or wrapped), 0)
End Function

下列範例接著會使用此 shift-and-wrap 方法來計算先前範例中使用的結構雜湊碼 Point

using System;

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

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

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

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

    public override int GetHashCode()
    {
        return ShiftAndWrap(x.GetHashCode(), 2) ^ y.GetHashCode();
    }

    private int ShiftAndWrap(int value, int positions)
    {
        positions = positions & 0x1F;

        // Save the existing bit pattern, but interpret it as an unsigned integer.
        uint number = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0);
        // Preserve the bits to be discarded.
        uint wrapped = number >> (32 - positions);
        // Shift and wrap the discarded bits.
        return BitConverter.ToInt32(BitConverter.GetBytes((number << positions) | wrapped), 0);
    }
}

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

        pt = new Point(8, 5);
        Console.WriteLine(pt.GetHashCode());
   }
}
// The example displays the following output:
//       28
//       37
open System

[<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 this.GetHashCode() =
        this.ShiftAndWrap(x.GetHashCode(), 2) ^^^ y.GetHashCode()

    member _.ShiftAndWrap(value, positions) =
        let positions = positions &&& 0x1F

        // Save the existing bit pattern, but interpret it as an unsigned integer.
        let number = BitConverter.ToUInt32(BitConverter.GetBytes value, 0)
        // Preserve the bits to be discarded.
        let wrapped = number >>> (32 - positions)
        // Shift and wrap the discarded bits.
        BitConverter.ToInt32(BitConverter.GetBytes((number <<< positions) ||| wrapped), 0)

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

let pt2 = Point(8, 5)
printfn $"{pt2.GetHashCode()}"
// The example displays the following output:
//       28
//       37
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 ShiftAndWrap(x.GetHashCode(), 2) XOr y.GetHashCode()
    End Function 
    
    Private Function ShiftAndWrap(value As Integer, positions As Integer) As Integer
        positions = positions And &h1F
      
        ' Save the existing bit pattern, but interpret it as an unsigned integer.
        Dim number As UInteger = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0)
        ' Preserve the bits to be discarded.
        Dim wrapped AS UInteger = number >> (32 - positions)
        ' Shift and wrap the discarded bits.
        Return BitConverter.ToInt32(BitConverter.GetBytes((number << positions) Or wrapped), 0)
    End Function
End Structure 

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 the following output:
'       28
'       37

備註

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

注意

如需雜湊表和一些其他雜湊程式碼演算法如何使用雜湊碼的資訊,請參閱 Wikipedia 中的 雜湊函 式專案。

兩個相等的 物件會傳回相等的雜湊碼。 不過,反轉不是 true:等號雜湊碼不表示物件相等,因為不同的 (不相等) 物件可以有相同的雜湊碼。 此外,.NET 不保證方法的預設實 GetHashCode 作,而且此方法傳回的值在 .NET 實作之間可能會不同,例如不同版本的 .NET Framework 和 .NET Core,以及 32 位和 64 位平臺等平臺。 基於這些原因,請勿使用此方法的預設實作做為雜湊目的的唯一物件識別碼。 下列兩個結果如下:

  • 您不應該假設相等雜湊碼表示物件相等。

  • 您絕對不應該保存或使用其建立所在應用程式域以外的雜湊程式碼,因為相同的物件可能會跨應用程式域、進程和平臺進行雜湊。

警告

雜湊程式碼的目的是為了在以雜湊表為基礎的集合中有效插入和查閱。 雜湊程式碼不是永久值。 基於這個原因:

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

方法 GetHashCode 可由衍生型別覆寫。 如果未 GetHashCode 覆寫,則會呼叫 Object.GetHashCode 基類的 方法來計算參考型別的雜湊碼,其會根據物件的參考來計算雜湊程式碼;如需詳細資訊,請參閱 RuntimeHelpers.GetHashCode 。 換句話說,方法傳 true 回的 ReferenceEquals 兩個 物件具有相同的雜湊碼。 如果實值型別未覆寫 GetHashCodeValueType.GetHashCode 基類的 方法會使用反映,根據類型欄位的值來計算雜湊碼。 換句話說,欄位具有相等值的實值型別具有相等的雜湊碼。 如需覆寫 GetHashCode 的詳細資訊,請參閱一節。

警告

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

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

Windows 執行階段注意事項

當您在Windows 執行階段中的類別上呼叫 GetHashCode 方法時,它會為未覆寫 GetHashCode 的類別提供預設行為。 這是.NET Framework為Windows 執行階段 (提供支援的一部分,請參閱.NET Framework Windows Store Apps 和 Windows 執行階段) 的支援。 Windows 執行階段中的類別不會繼承 Object ,而且目前不會實作 GetHashCode 。 不過,當您在 C# 或Visual Basic程式碼中使用 、 和 方法時,它們似乎具有 ToStringEquals(Object)GetHashCode 方法,而.NET Framework會提供這些方法的預設行為。

注意

Windows 執行階段以 C# 或 Visual Basic 撰寫的類別可以覆寫 GetHashCode 方法。

給繼承者的注意事項

雜湊函式可用來快速產生對應至物件值的數位 (雜湊碼) 。 雜湊函式通常專屬於每個類型,而且針對唯一性,必須至少使用其中一個實例欄位做為輸入。 雜湊碼不應使用靜態欄位的值來計算。

對於衍生自 Object 的類別,只有在衍生類別定義相等為參考相等時, GetHashCode 方法才能委派至基類 GetHashCode() 實作。 參考型別的預設實 GetHashCode() 作會傳回雜湊碼,相當於 方法所傳回的 GetHashCode(Object) 雜湊碼。 您可以覆寫 GetHashCode() 不可變的參考型別。 一般而言,針對可變動的參考類型,您應該只在下列情況下覆寫 GetHashCode()

  • 您可以從不可變動的欄位計算雜湊碼;或
  • 您可以在物件包含在依賴其雜湊碼的集合中時,確保可變物件的雜湊碼不會變更。

否則,您可能會認為可變動的物件在雜湊表中遺失。 如果您選擇覆寫 GetHashCode() 可變動的參考類型,您的檔應該清楚指出類型的使用者不應該在物件儲存在雜湊表中時修改物件值。

針對實值型別, GetHashCode() 提供使用反映的預設雜湊程式碼實作。 您應該考慮覆寫它,以提升效能。

如需以各種方式計算雜湊碼的詳細資訊和範例,請參閱一節。

雜湊函式必須具有下列屬性:

  • 如果兩個物件相等, GetHashCode() 則每個物件的 方法必須傳回相同的值。 不過,如果兩個物件不相等, GetHashCode() 則兩個物件的方法不需要傳回不同的值。

GetHashCode()- 物件的方法必須一致地傳回相同的雜湊碼,只要物件狀態沒有修改,判斷物件的System.Object.Equals方法的傳回值。 請注意,這僅適用于應用程式的目前執行,而且如果再次執行應用程式,則可以傳回不同的雜湊碼。

  • 為了達到最佳效能,雜湊函式應該為所有輸入產生偶數分佈,包括大量叢集的輸入。 隱含的是,對物件狀態的小型修改應該會對產生的雜湊程式碼進行大量修改,以獲得最佳雜湊表效能。

  • 雜湊函式的計算成本應該較低。

  • 方法 GetHashCode() 不應該擲回例外狀況。

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

在類別上提供良好的雜湊函式,可能會大幅影響將這些物件新增至雜湊表的效能。 在提供雜湊函式良好實作的雜湊資料表中,搜尋元素需要固定時間 (,例如 O (1) 作業) 。 在雜湊函式實作不佳的雜湊資料表中,搜尋的效能取決於雜湊表中的專案數 (,例如 O () n 作業,其中 n 是雜湊表) 的專案數。 惡意使用者可以輸入資料來增加衝突數目,這可能會在下列情況下大幅降低相依于雜湊表的應用程式效能:

  • 當雜湊函式產生頻繁衝突時。

  • 當雜湊表中大量的物件產生雜湊碼等於或大約等於另一個雜湊碼時。

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

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

適用於

另請參閱