Почему мне никогда не приходится переопределять GetHashCode при использовании словарей в личных классах? - PullRequest
2 голосов
/ 18 сентября 2011

Всегда кажется, что он просто "работает", не делая ничего.

Единственное, о чем я могу думать, - это то, что у каждого класса есть скрытый вид статического идентификатора, который Object.GetHashCode использует. (также кто-нибудь знает, как реализован Object.GetHashCode? Я не смог найти его в .NET Reflector)

Я никогда не переопределял GetHashCode, но я читал вокруг, и люди говорят, что вам нужно только при переопределении Equals и предоставлении настраиваемой проверки на равенство для вашего приложения, так что, думаю, я в порядке?

Я все еще хотел бы знать, как работает магия, хотя = P

Ответы [ 5 ]

3 голосов
/ 18 сентября 2011

Кажется, что это всегда просто "работает" без необходимости что-либо делать.

Вы не сказали нам, используете ли вы типы значений или ссылочные типы для ваших ключей.

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

Единственное, о чем я могу думать, это то, что у каждого класса есть скрытый вид статического идентификатора, который использует Object.GetHashCode.

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

(кроме того, кто-нибудь знает, как реализован Object.GetHashCode? Я не смог найтиэто в .NET Reflector)

Это деталь реализации, которую вы никогда не должны знать и никогда не полагаетесь на нее.Он может измениться в любой момент.

Я никогда не переопределял GetHashCode, но я читал вокруг, и люди говорят, что вам нужно только при переопределении Equals и предоставлении пользовательской проверки на равенство для вашего приложения, так что я думаюЯ в порядке?

Ну, для вас нормально равенство по умолчанию?Если нет, переопределите Equals и GetHashCode или implmenet IEqualityComparer<T> для вашего T.

Я все еще хотел бы знать, как работает магия, хотя = P

Каждый объект имеет Equals и GetHashCode.Реализации по умолчанию следующие:

  1. Для типов значений Equals - равенство значений.
  2. Для ссылочных типов Equals - равенство ссылок.
  3. Для типов значений GetHashCode основано на полях (опять же, не обязательно на всех!).
  4. Для ссылочных типов GetHashCode основано на ссылке.

Если вы используете перегрузку конструктора Dictionary, который не принимает IEqualityComparer<T> для вашего T, он будет использовать EqualityComparer<T>.Default.Это IEqualityComparer<T> просто использует Equals и GetHashCode.Итак, если вы не переопределили их, вы получите реализации, как определено выше.Если вы переопределите Equals и GetHashCode, то это то, что EqualityComparer<T>.Default будет использовать.

В противном случае передайте пользовательскую реализацию IEqualityComparer<T> в конструктор для Dictionary.

1 голос
/ 18 сентября 2011

Используете ли вы свои пользовательские классы в качестве ключей или значений? Если вы используете их только для значений, то GetHashCode не имеет значения.

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

Документация для Object.GetHashCode довольно clear :

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

0 голосов
/ 18 сентября 2011

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

Способ работы хеш-таблиц заключается в том, что каждый объект реализует функцию для вычисления хеш-кода, как мы надеемся, как можно более распределенного среди intспектр.Два разных объекта могут создавать один и тот же хэш-код, но экземпляр объекта, учитывая, что его данные всегда должны создавать один и тот же хэш-код.Следовательно, они не уникальны и не должны использоваться для равенства.Хеш-таблица выделяет массив размером n (намного меньше, чем диапазон int), и когда объект добавляется в хеш-таблицу, он вызывает GetHashCode, а затем его mod'd (%) в зависимости от размера выделенного массива.Для коллизий в таблице, как правило, список объектов объединяется в цепочку.Поскольку вычисление хеш-кодов должно быть очень быстрым, поиск выполняется быстро - перейдите к смещению массива и пройдитесь по цепочке.Чем больше массив (больше памяти), тем меньше коллизий и тем быстрее поиск.

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

Если вам нужно сравнить два элементазатем переопределить равно.Если вам нужно, чтобы объект был сортируем (что необходимо для отсортированных списков), переопределите IComparable.

Надеюсь, что это поможет объяснить разницу.

0 голосов
/ 18 сентября 2011

Это действительно зависит от вашего определения равенства.

class Person
{
    public string Name {get; set;}
}

void Test()
{
    var joe1 = new Person {Name="Joe"};
    var joe2 = new Person {Name="Joe"};

    Assert.AreNotEqual(joe1, joe2);
}

Если у вас есть другое определение равенства, вы должны переопределить Equals & GetHashCode, чтобы получить соответствующее поведение.

0 голосов
/ 18 сентября 2011
Реализации

Object Equals() и GetHashCode() (которые вы наследуете) сравниваются по ссылке.
Object.GetHashCode реализовано в собственном коде;Вы можете увидеть это в SSCLI (Rotor).

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

Вам нужно только переопределить ихесли вы хотите сравнить по значению - если вы хотите, чтобы разные экземпляры с одинаковыми свойствами сравнивались равными.

...