Что возвращает метод GetHashCode в C#?

«Что возвращает метод GetHashCode в C#?» — вопрос из категории C# Core, который задают на 25% собеседований C# Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Метод GetHashCode() возвращает 32-битное целое число (int), которое представляет хеш-код объекта. Это значение используется в качестве ключа в хеш-таблицах, таких как Dictionary<TKey, TValue> и HashSet<T>, для быстрого определения местоположения объекта и сравнения.

Ключевые контракты метода:

  1. Консистентность: Для одного и того же объекта, состояние которого не изменилось, метод должен возвращать одно и то же значение в течение всего времени жизни приложения.
  2. Согласованность с Equals: Если два объекта равны согласно методу Equals(object), то их хеш-коды, возвращаемые GetHashCode(), должны быть одинаковыми.
  3. Коллизии допускаются: Обратное утверждение неверно. Разные объекты (для которых Equals возвращает false) могут иметь одинаковый хеш-код (коллизия). Однако хорошая реализация стремится минимизировать коллизии для производительности.

Пример использования:

string str1 = "hello";
string str2 = "hello";
string str3 = "world";

Console.WriteLine(str1.GetHashCode() == str2.GetHashCode()); // True: одинаковые строки
Console.WriteLine(str1.GetHashCode() == str3.GetHashCode()); // False (с высокой вероятностью): разные строки

Важные замечания:

  • Для ссылочных типов по умолчанию GetHashCode() возвращает значение, основанное на идентификаторе потока и внутреннем индексе объекта в управляемой куче. Это не является адресом в памяти, но уникально для объекта в рамках процесса.
  • Для изменяемых (mutable) типов переопределять GetHashCode() крайне не рекомендуется. Если объект, используемый как ключ в словаре, изменяется после добавления, его хеш-код может измениться, и объект станет недоступен для поиска, что приведет к ошибкам.
  • При переопределении Equals(object) обязательно переопределяйте и GetHashCode(), чтобы соблюсти контракт. Хорошая реализация использует те же поля, что и Equals.

    public class Person
    {
    public int Id { get; init; }
    public string Name { get; init; }
    
    public override bool Equals(object obj) => obj is Person p && Id == p.Id && Name == p.Name;
    public override int GetHashCode() => HashCode.Combine(Id, Name); // Используем встроенный комбинатор
    }