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 тип может работать неправильно.

Применяется к

См. также раздел