Object.GetHashCode 方法
定义
重要
一些信息与预发行产品相关,相应产品在发行之前可能会进行重大修改。 对于此处提供的信息,Microsoft 不作任何明示或暗示的担保。
作为默认哈希函数。
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 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 为需要快速检查对象相等性的算法提供此哈希代码。
备注
有关哈希表和一些其他哈希代码算法如何使用哈希代码的信息,请参阅维基百科中的 哈希函数 条目。
两个相等的返回哈希代码的对象相等。 但是,相反不是真的:相等哈希代码并不意味着对象相等,因为不同的 (不相等) 对象可以具有相同的哈希代码。 此外,.NET 不保证该方法的默认实现GetHashCode,此方法返回的值在 .NET 实现之间可能有所不同,例如不同版本的 .NET Framework 和 .NET Core,以及 32 位和 64 位平台等平台。 出于这些原因,请勿将此方法的默认实现用作哈希处理的唯一对象标识符。 以下两个后果如下:
不应假定等于哈希代码表示对象相等。
不应在创建应用程序域的应用程序域之外保留或使用哈希代码,因为同一对象可能会跨应用程序域、进程和平台进行哈希处理。
警告
哈希代码用于在基于哈希表的集合中高效插入和查找。 哈希代码不是永久值。 出于此原因:
- 不要序列化哈希代码值或将它们存储在数据库中。
- 请勿使用哈希代码作为键从键控集合中检索对象。
- 不要跨应用程序域或进程发送哈希代码。 在某些情况下,哈希代码可以按进程或按应用程序域计算。
- 如果需要加密强哈希,请不要使用哈希代码而不是加密哈希函数返回的值。 对于加密哈希,请使用派生自或System.Security.Cryptography.KeyedHashAlgorithm类的System.Security.Cryptography.HashAlgorithm类。
- 请勿测试哈希代码的相等性,以确定两个对象是否相等。 (Unequal 对象可以具有相同的哈希代码。) 若要测试相等性,请调用 ReferenceEquals 或 Equals 方法。
GetHashCode方法可由派生类型重写。 如果未 GetHashCode 重写,则通过调用 Object.GetHashCode 基类的方法来计算引用类型的哈希代码,该方法基于对象的引用计算哈希代码;有关详细信息,请参阅 RuntimeHelpers.GetHashCode。 换句话说,该方法返回true
的两个对象ReferenceEquals具有相同的哈希代码。 如果未重写 GetHashCode值类型, ValueType.GetHashCode 则基类的方法使用反射基于类型字段的值计算哈希代码。 换句话说,字段具有相等值的值类型具有相等哈希代码。 有关重写 GetHashCode的详细信息,请参阅“继承者说明”部分。
警告
如果重写 GetHashCode 该方法,还应重写 Equals,反之亦然。 如果重写 Equals 的方法在测试两个对象时返回 true
相等性,则重写 GetHashCode 的方法必须为这两个对象返回相同的值。
如果用作哈希表中键的对象不提供有用的实现,则可以通过向类构造函数的Hashtable重载提供IEqualityComparer实现GetHashCode来指定哈希代码提供程序。
Windows 运行时说明
在Windows 运行时中对类调用GetHashCode该方法时,它会为不重写GetHashCode的类提供默认行为。 这是.NET Framework为Windows 运行时 (提供的支持的一部分,请参阅.NET Framework对 Windows 应用商店应用的支持和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 类型可能无法正常工作。