Страницы

Поиск по вопросам

четверг, 1 ноября 2018 г.

GetHashCode — вычисление хэш

Мне интересно узнать, каким образом формируется хэш объекта в C#. Например, есть тестовый класс:
public class A { public int b = 0; public int c; }
Далее где-нибудь в коде:
A a1 = new A(); A a2 = new A(); a1.c = 10; a2.c = 10;
a1 и a2, по логике вещей, совершенно идентичны. Но для C# нет. Их хэши различны. Конечно же, понятно, что это различие может быть оправдано, например, различием адресами в памяти, ну или временем создания, в конце концов :). Так вот хочется точно узнать, как же формируются хэши объектов и чем обосновывается их различие для идентичных пользовательских объектов.


Ответ

Для начала, поскольку ваш класс A не переопределяет GetHashCode(), эта функция наследуется от object
Точный алгоритм вычисления GetHashCode() для object не специфицирован
Реализация GetHashCode по умолчанию не гарантирует уникальные значения для различных объектов. Кроме того, .NET Framework не гарантирует, что реализация GetHashCode по умолчанию, в том числе значения, возвращаемые ей, не поменяются при смене версии фреймворка. Соответственно, значение функции GetHashCode по умолчанию не должно быть использована как уникальный идентификатор объекта с целью хеширования.
То, что вам два объекта кажутся идентичными, не гарантирует, что у них при реализации по умолчанию совпадут хеш-коды. С другой стороны, посудите сами: если у двух людей совпадает имя и фамилия, это ведь ещё не значит, что это один и тот же человек? Так и .NET framework, если у двух экземпляров класса A совпадают все поля, ещё не считает эти экземпляры одним и тем же объектом. Соответственно, по умолчанию используется что угодно -- например, адрес при создании или время создания, документация не говорит о точном методе. Если вас не устраивает поведение по умолчанию, вы должны его перекрыть.
Если ваш класс не использует операцию Equals и не является ключом в хеш-таблицах, в принципе можно ничего и не делать, вас должно устраивать и поведение по умолчанию. Но если ваш класс служит ключом, вам необходимо переопределить как Equals, так и GetHashCode(), причём согласованным образом: если x.Equals(y) возвращает true, то и хеш-коды у x и y обязаны совпадать.
Отсюда следует, что вы должны переопределять функцию GetHashCode() вместе с функцией Equals. Например, так:
public class A { public int b = 0; public int c;
public override bool Equals(object obj) { return Equals(obj as A); }
public bool Equals(A that) { if (that == null) return false; return this.b == that.b && this.c == that.c; }
public override int GetHashCode() { return b.GetHashCode() * 13 + c.GetHashCode(); } }

В C#, в отличие от многих других языков, есть объекты двух типов: объекты, полностью определяющиеся своими атрибутами (то есть, полями), и объекты, представляющие собой самостоятельную сущность, не сводящуюся лишь к значениям атрибутов.
Примером объекта первого типа являются, например, числа: если вы пишете
x = 5; y = 5;
-- нет никакой разницы между первой и второй пятёркой, это одна и та же сущность. Подобные штуки называются в .NET типами-значениями и вводятся ключевым словом struct. Для них метод Equals по умолчанию сравнивает значения всех полей, и если они равны, заключает, что и структуры равны, одинаковы. Поэтому структуры можно копировать по значению, структуру можно безболезненно заменить на её копию. (Метод GetHashCode по умолчанию определён соответствующим образом: для равных структур он гарантированно возвращает одинаковый хеш.)
Примером другой сущности может служить, например, автомобиль, атрибутами которого являются марка, год выпуска, мощность двигателя и т. п. Два автомобиля с одинаковыми техническими параметрами -- это всё же разные автомобили. Такие объекты в C# называются ссылочными типами и вводятся ключевым словом class. Для классов, соответственно, реализация Equals по умолчанию не имеет права предполагать, что одинаковые значения полей автоматически означают равенство, каждый экземпляр класса считается уникальным
Подумайте, не нужна ли вам на самом деле вместо класса A структура?
Вопрос о том, определяемся ли мы все, люди, лишь нашим набором параметров, или в нас есть что-то вне этого, не так уж прост.

Комментариев нет:

Отправить комментарий