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

注释

有关哈希表和一些其他哈希代码算法如何使用哈希代码的信息,请参阅维基百科中的 哈希函数 条目。

两个相等的对象返回相等的哈希代码。 但是,相反情况并非如此:相等哈希代码并不表示对象相等,因为不同的(不相等)对象可以具有相同的哈希代码。 此外,.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 类型可能无法正常工作。

适用于

另请参阅