Переопределение GetHashCode ()

ВЭта статьяДжон Скит отметил, что он обычно использует этот тип алгоритма для переопределенияGetHashCode().

public override int GetHashCode()
{
  unchecked // Overflow is fine, just wrap
  {
    int hash = 17;
    // Suitable nullity checks etc, of course :)
    hash = hash * 23 + Id.GetHashCode();
    return hash;
  }
}

Теперь я попробовал использовать это, но Resharper говорит мне, что методGetHashCode() должно быть хэширование с использованием только полей только для чтения (хотя он прекрасно компилируется). Что было бы хорошей практикой, потому что сейчас у меня не может быть моих полей только для чтения?

Я попытался сгенерировать этот метод с помощью Resharper, вот результат.

public override int GetHashCode()
{
  return base.GetHashCode();
}

Честно говоря, это не очень помогает ...

 Ria14 июл. 2012 г., 07:30
может быть дубликатом:Overriding GetHashCode
 Pacane14 июл. 2012 г., 07:31
@Ria Нигде в этом вопросе люди не говорят о полях только для чтения.
 tom redfern14 февр. 2017 г., 18:12
 drf14 июл. 2012 г., 07:49
Вы можете обратиться кthis article в блоге Эрика Липперта. В частности, обратите внимание на раздел «Правило: целое число, возвращаемое GetHashCode, никогда не должно изменяться, пока объект содержится в структуре данных, которая зависит от стабильности хеш-кода». Он пишет, что «допустимо, хотя и опасно, создавать объект, значение хеш-кода которого может изменяться по мере изменения полей объекта».

Ответы на вопрос(3)

Обратите внимание, что ваш GetHashCode должен идти рука об руку с вашим методом Equals. И если вы можете просто использовать равенство ссылок (когда у вас никогда не будет двух разных экземпляров вашего класса, которые могут быть равны), тогда вы можете безопасно использовать Equals и GetHashCode, которые унаследованы от Object. Это будет работать намного лучше, чем простоreturn 1 из GetHashCode.

Решение Вопроса

Если все ваши поля изменчивы, и вы должны реализоватьGetHashCode Боюсь, что эта реализация вам нужна.

public override int GetHashCode() 
{ 
    return 1; 
} 

Да, это неэффективно, но это по крайней мере правильно.

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

Обратите внимание, что если все объекты возвращают один и тот же HashCode 1, это в основном означает, что все объекты помещаются в одно и то же ведро в HashSet или Dictionary. Таким образом, в HashSet или в словаре всегда есть только одна корзина. При попытке поиска объекта он выполнит проверку на равенство для каждого из объектов внутри единственного сегмента. Это похоже на поиск в связанном списке.

Кто-то может поспорить, что реализация хэш-кода на основе изменяемых полей может быть полезной, если мы сможем убедиться, что поля никогда не меняются после добавления объектов в коллекцию HashCode или Dictionary. Мое личное мнение таково, что это подвержено ошибкам. Кто-то, кто захватит ваш код два года спустя, может не знать об этом и случайно нарушит код.

 14 июл. 2012 г., 08:31
@ ja72struct та же. Вы должны сгенерировать хэш-код из неизменяемого поля.
 14 июл. 2012 г., 08:25
В этом случае вообще не нужно переопределять GetHashCode, реализация по умолчанию будет работать намного лучше. Хотя хэш не будет зависеть от содержимого объекта, по меньшей мере он будет отличаться для разных объектов.
 14 июл. 2012 г., 08:29
@AntonTykhyy Если мы используем GetHashCode () по умолчанию, два объекта с одним и тем же полем Id = 1 будут иметь разный HashCode и, следовательно, искать из разных сегментов. Я предполагаю, что в этом случае мы хотим, чтобы они имели одинаковый хэш-код.
 14 июл. 2012 г., 08:47
@HarveyKwok: ну, это действительно зависит от того, являются ли объекты уравняемыми (где равенство основано на равенстве изменяемых полей) или нет. Если да, то вы правы, GetHashCode по умолчанию не подходит. Если нет, то идентичность объекта не зависит от значений его (изменяемых) полей, и GetHashCode по умолчанию в порядке. Однако изменяемые уравниваемые объекты не являются хорошей идеей, по той же причине, по которой вычисление GetHashCode на основе изменяемых полей не является хорошей идеей.
 14 июл. 2012 г., 08:24
Как насчетstrucs? Должны ли поля быть неизменными? Мне трудно изменить их поля при сохранении вDictionary.

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

Например

public class A
{
    // TODO Equals override

    public override int GetHashCode()
    {
        return 21313;
    } 
}

public class B
{
    // TODO Equals override

    public override int GetHashCode()
    {
        return 35507;
    } 
}

Тогда, если у меня естьDictionary<object, TValue> содержащие случаиA, B и другие типы, производительность поиска будет лучше, чем если бы все реализацииGetHashCode вернул то же числовое значение.

Следует также отметить, что я использую простые числа, чтобы получить лучшее распределение.

В соответствии с комментариями я предоставил образец LINQPadВот который демонстрирует разницу в производительности между использованиемreturn 1 для разных типов и возвращая разные значения для каждого типа.

 17 февр. 2014 г., 17:50
Можете ли вы расширить свое последнее предложение?
 17 февр. 2014 г., 22:00
Вспоминая ответ, последнее предложение не подходит для этой ситуации. При реализацииGetHashCode() метод с полями только для чтения, однако использование простых чисел гарантирует, что вы с большей вероятностью достигнете лучшего распределения по сегментам словаря или хэш-набора. Есть много статей в Интернете, ноthis SO question должен начать вас.
 24 нояб. 2014 г., 12:30
@Lukazoid Извините, я обычно делаю комментарии - забыл на этот раз!
 21 нояб. 2014 г., 10:17
Было бы неплохо объяснить понижение рейтинга, я считаю, что это разумный подход к различным типам.
 24 нояб. 2014 г., 12:35
@Lukazoid: проблема с этим решением в том, что оно поощряетO(N^2) поведение. Отсутствие столкновений между различными классами вряд ли поможет, и это только помогает, если код смешивает типы и подрывает систему типов - то есть код, который вы действительно не хотите помогать.

Ваш ответ на вопрос