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
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.
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
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);
}
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

В следующем примере используется этот метод сдвига и переноса для вычисления хэш-кода 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
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.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 конструктора класса.

примечания для среда выполнения Windows

при вызове GetHashCode метода класса в среда выполнения Windows он предоставляет поведение по умолчанию для классов, которые не переопределяются GetHashCode . это является частью поддержки, предоставляемой платформа .NET Framework для среда выполнения Windows (см. платформа .NET Framework поддержка Windows приложений магазина и среда выполнения Windows). классы в среда выполнения Windows не наследуют Object и в настоящее время не реализуют GetHashCode . однако они выглядят как ToString Equals(Object) методы, и GetHashCode при их использовании в коде C# или Visual Basic, а платформа .NET Framework предоставляет поведение по умолчанию для этих методов.

Примечание

Windows классы среды выполнения, написанные на языке C# или Visual Basic, могут переопределять GetHashCode метод.

Примечания для тех, кто наследует этот метод

Хэш-функция используется для быстрого создания числа (хэш-кода), соответствующего значению объекта. Хэш-функции обычно относятся к каждому типу, и для уникальности в качестве входных данных должно использоваться хотя бы одно из полей экземпляра. Хэш-коды не должны вычисляться с помощью значений статических полей.

Для классов, производных от Object , GetHashCode метод может делегировать реализацию базового класса GetHashCode() только в том случае, если производный класс определяет равенство на равенство ссылок. Реализация по умолчанию GetHashCode() для ссылочных типов возвращает хэш-код, эквивалентный тому, который возвращается GetHashCode(Object) методом. GetHashCode()Для неизменяемых ссылочных типов можно переопределить. Как правило, для изменяемых ссылочных типов следует переопределить GetHashCode() только в том случае, если: — Хэш-код можно вычислить из полей, которые не являются изменяемыми. ни — Можно гарантировать, что хэш-код изменяемого объекта не изменится, пока объект содержится в коллекции, зависящей от его хэш-кода.

В противном случае может показаться, что изменяемый объект будет потерян в хэш-таблице. Если выбрать переопределение GetHashCode() для изменяемого ссылочного типа, в документации должна быть ясно, что пользователи вашего типа не должны изменять значения объектов, пока объект хранится в хэш-таблице.

Для типов значений GetHashCode() предоставляет реализацию хэш-кода по умолчанию, использующую отражение. Рекомендуется переопределять его для повышения производительности.

Дополнительные сведения и примеры, которые вычисляют хэш-коды различными способами, см. в разделе "примеры".

Хэш-функция должна иметь следующие свойства: — Если два объекта сравнивают как равные, GetHashCode() метод для каждого объекта должен возвращать одно и то же значение. Однако если два объекта не равны как равные, то GetHashCode() методы для этих двух объектов не должны возвращать разные значения.

GetHashCode() Метод для объекта должен постоянно возвращать тот же хэш-код, если в состоянии объекта нет изменений, определяющих возвращаемое значение метода System. Object. Equals объекта. Обратите внимание, что это справедливо только для текущего выполнения приложения. при повторном запуске приложения может возвращаться другой хэш-код.

Для лучшей производительности хэш-функция должна создать равномерное распределение для всех входных данных, включая входные данные, которые сильно кластеризованы. Следствием является то, что небольшие изменения в состоянии объекта должны привести к значительным изменениям в результирующем хэш-коде для лучшей производительности хэш-таблицы.

-Функции хэширования должны быть недорогими для вычислений.

GetHashCode() Метод не должен создавать исключения.

Например, реализация GetHashCode() метода, предоставляемого String классом, возвращает идентичные хэш-коды для одинаковых строковых значений. Поэтому два String объекта возвращают один и тот же хэш-код, если они представляют одно и то же строковое значение. Кроме того, метод использует все символы в строке для создания достаточно случайного распределения выходных данных, даже если входные данные кластеризованы в определенные диапазоны (например, многие пользователи могут иметь строки, содержащие только младшие символы ASCII 128, даже если строка может содержать любой из 65 535 символов Юникода).

Предоставление хорошей хэш-функции для класса может значительно повлиять на производительность при добавлении этих объектов в хэш-таблицу. В хэш-таблице с ключами, обеспечивающими хорошую реализацию хэш-функции, поиск элемента занимает постоянное время (например, операция O (1)). В хэш-таблице с плохой реализацией хэш-функции производительность поиска зависит от числа элементов в хэш-таблице (например, операции O ( n ), где n — число элементов в хэш-таблице). Пользователь-злоумышленник может вводить данные, увеличивающие количество конфликтов, что может значительно снизить производительность приложений, зависящих от хэш-таблиц, при следующих условиях. — Когда хэш-функции вызывают частые конфликты.

— Когда большая часть объектов в хэш-таблице создает хэш-коды, которые равны или приблизительно равны друг другу.

— При вводе пользователями данных, из которых вычисляются хэш-код.

Производные классы, переопределяющие, GetHashCode() также должны переопределяться Equals(Object) , чтобы убедиться, что два объекта считаются равными, имеют одинаковый хэш-код. в противном случае Hashtable тип может работать неправильно.

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

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