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-разрядными платформами. По этим причинам не используйте реализацию этого метода по умолчанию в качестве уникального идентификатора объекта для хэширования. Из этого следует два последствия:
- Не следует предполагать, что равные хэш-коды подразумевают равенство объектов.
- Никогда не следует сохранять хэш-код за пределами домена приложения, в котором он был создан, так как один и тот же объект может хэшировать между доменами приложений, процессами и платформами.
Предупреждение
Хэш-код предназначен для эффективной вставки и поиска в коллекциях, основанных на хэш-таблице. Хэш-код не является постоянным значением. По этой причине:
- Не сериализуйте значения хэш-кода или не храните их в базах данных.
- Не используйте хэш-код в качестве ключа для извлечения объекта из коллекции ключей.
- Не отправляйте хэш-коды между доменами или процессами приложений. В некоторых случаях хэш-коды могут вычисляться на основе каждого процесса или домена для каждого приложения.
- Не используйте хэш-код вместо значения, возвращаемого функцией шифрования хэширования, если требуется криптографически сильный хэш. Для криптографических хэшей используйте класс, производный от System.Security.Cryptography.HashAlgorithm класса или System.Security.Cryptography.KeyedHashAlgorithm класса.
- Не проверяйте равенство хэш-кодов, чтобы определить, равны ли два объекта. (Неравные объекты могут иметь идентичные хэш-коды.) Чтобы проверить равенство, вызовите ReferenceEquals метод или Equals метод.
Метод GetHashCode можно переопределить производным типом. Если GetHashCode не переопределено, хэш-коды для ссылочных типов вычисляются путем вызова Object.GetHashCode метода базового класса, который вычисляет хэш-код на основе ссылки объекта; дополнительные сведения см. в разделе RuntimeHelpers.GetHashCode. Другими словами, два объекта, для которых метод ReferenceEquals возвращает true, имеют одинаковые хэш-коды. Если типы значений не переопределяют GetHashCode, метод ValueType.GetHashCode базового класса использует рефлексию для вычисления хэш-кода на основе значений полей типа. Другими словами, типы значений, поля которых имеют равные значения, имеют равные хэш-коды. Дополнительные сведения о переопределении GetHashCode см. в разделе "Замечания для наследников".
Предупреждение
При переопределении 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 символов Юникода).
Предоставление хорошей хэш-функции в классе может значительно повлиять на производительность добавления этих объектов в хэш-таблицу. В хэш-таблице с ключами, обеспечивающими хорошую реализацию хэш-функции, поиск элемента занимает постоянное время (например, операция O(1). В хэш-таблице с плохой реализацией хэш-функции производительность поиска зависит от количества элементов в хэш-таблице (например, операции O(n), где n число элементов в хэш-таблице). Злоумышленник может вводить данные, которые увеличивают количество конфликтов, что может значительно снизить производительность приложений, зависящих от хэш-таблиц, в следующих условиях:
Когда хэш-функции создают частые столкновения.
Если большая доля объектов в хэш-таблице создает хэш-коды, равные или приблизительно равные друг другу.
Когда пользователи вводили данные, из которых вычисляется хэш-код.
Производные классы, которые переопределяются, также должны переопределяться GetHashCode()Equals(Object) , чтобы гарантировать, что два объекта, которые считаются равными, имеют одинаковый хэш-код; в противном случае Hashtable тип может работать неправильно.