Обзор вопросов по языка програмиированию C# и технологии NET
GetHashCode()
— метод System.Object, который возвращает целое число (int
), представляющее объект в виде хэш-кода.Dictionary
, HashSet
, Hashtable
).Microsoft рекомендует соблюдать несколько правил:
Согласованность
Equals
), то их GetHashCode()
должны совпадать.Необязательная уникальность
Стабильность
Equals
меняется, хэш-код тоже должен меняться соответствующе, иначе коллекции станут неконсистентными.Пример:
var dict = new Dictionary<Person, string>();
var p1 = new Person("John", 30);
dict[p1] = "Engineer";
p1.Age = 31; // если Age участвует в Equals/GetHashCode
// dict[p1] теперь может не найтись в коллекции
int
, double
, DateTime
) — возвращают хэш, основанный на значении.Object
) — возвращают хэш по ссылке объекта, т.е. разные объекты → разные хэши (даже если их содержимое одинаково, пока не переопределён GetHashCode
).GetHashCode()
так, чтобы одинаковые строки имели одинаковый хэш-код.GetHashCode()
Коллекции с хэшированием
Dictionary<TKey, TValue>
и HashSet<T>
используют хэш-коды для быстрого поиска.Операции Contains
, Remove
Equals
для подтверждения.LINQ и другие алгоритмы
Distinct
, GroupBy
).Пример для Dictionary<TKey,TValue>
:
GetHashCode()
→ целое число.hash % buckets.Length
.Equals
.Важное:
Dictionary
, объект может «потеряться».GetHashCode()
Equals()
, нужно переопределять GetHashCode()
.class Person
:class Person
{
public string Name { get; }
public int Age { get; }
public override bool Equals(object obj) =>
obj is Person p && p.Name == Name && p.Age == Age;
public override int GetHashCode() =>
HashCode.Combine(Name, Age);
}
HashCode.Combine
(C# 8.0+) для создания качественного хэша.Обязательность переопределения
Equals
, обязательно переопределять GetHashCode
.Mutable ключи
Коллизии
Ссылочные типы без переопределения
Стабильность
string
(для защиты от DoS атак).HashCode.Combine
или System.Collections.StructuralComparisons
для массивов/структур.Dictionary
var dict = new Dictionary<Person, string>();
dict[new Person("Alice", 25)] = "Engineer";
var key = new Person("Alice", 25);
Console.WriteLine(dict.ContainsKey(key)); // true, если Equals и GetHashCode согласованы
HashSet
var set = new HashSet<string>();
set.Add("Hello");
set.Add("Hello"); // не добавит повторно
Distinct
var numbers = new List<int> {1,2,2,3};
var distinct = numbers.Distinct(); // использует GetHashCode + Equals
Что делает GetHashCode()
?
Возвращает целое число для хэширования объекта, используемое в хэш-коллекциях.
Что важнее: Equals()
или GetHashCode()
?
Они связаны: если Equals
переопределён, GetHashCode
должен быть согласован.
Можно ли использовать изменяемый объект в качестве ключа Dictionary
?
Нельзя, если хэш-код зависит от изменяемых полей.
Что такое коллизия? Два разных объекта имеют одинаковый хэш-код.
Почему хэш-код объекта может изменяться между запусками приложения? В .NET Core строки используют случайное смещение для защиты от атак DoS.
Как безопасно реализовать GetHashCode()
для нескольких полей?
Использовать HashCode.Combine(field1, field2, ...)
.
Что произойдёт, если не переопределить GetHashCode()
при переопределении Equals()
?
Хэш-коллекции могут работать некорректно (не смогут найти объект по ключу).
✨Dvurechensky✨