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

466
04 февраля 2017, 07:04

Троелсен в своей книге, советует переопределить метод ToString() у класса(переопределенная версия возвращает строку из полей всего класса через пробел), а потом в методе GetHashCode вызывать this.ToString().GetHashCode().

Где-то читал критику такой реализации, например, вызов ToString() будет порождать мусор.

Правда ли, что она не очень хороша.

Какие могут быть альтернативы?

Answer 1

Проблема со строкой состоит в том, что GetHashCode() может вызываться очень часто, много тысяч раз в секунду. Например, при поиске элемента в HashSet<T> или поиске ключа в Dictionary<K, V> у каждого искомого элемента берётся хэшкод. Если при этом будет каждый раз создаваться временная, никому за пределами метода не нужная строка, их потом должен будет очистить сборщик мусора.

Поэтому функция GetHashCode должна быть по возможности быстрой и не создавать дополнительных объектов.

По поводу того, как же лучше считать хэшкод, я бы посоветовал немного переделанный код из ответа @Umed:

public override int GetHashCode()
{
    unchecked
    {
        int hash = 17;
        hash = hash * 23 + field1.GetHashCode(); // для поля типа-значения, например int
        hash = hash * 23 + field2.GetHashCode(); // то же самое
        if (field3 != null)                      // а это для поля ссылочного типа
            hash = hash * 23 + field3.GetHashCode();
        return hash;
    }
}

Вместо 17 и 23 можно взять другие разные простые числа.

Ещё по теме: Почему при переопределении Equals советуют также переопределять GetHashCode.

Answer 2

Как вариант, можно посчитать его как сумму хэшей его полей:

public override int GetHashCode()
{
    int result;
    uncheked
    {
        // если поле ссылочного типа,
        // то необходима проверка на null
        // если field1 будет null, то подставится 0
        result = this.field1?.GetHashCode() ?? 0;
        result += this.field2?.GetHashCode() ?? 0;
        //...
        result += this.fieldN.GetHashCode();
    }
    return result;
}

Либо через создание анонимного класса (подсмотрел в английской SO):

public override int GetHashCode()
{
    return new { this.field1, this.field2, /*... ,*/ this.fieldN }.GetHashCode();
}

Такой вариант тоже работает достаточно быстро, но за тем исключением, что каждый вызов порождает анонимный класс.

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

public override int GetHashCode()
{
    unchecked
    {
        int hash = 17;
        // если поле ссылочного типа,
        // то необходима проверка на null
        hash = hash * 23 + field1?.GetHashCode() ?? 0;
        // если это, например, int, то проверка ни к чему
        hash = hash * 23 + field3.GetHashCode();
        return hash;
    }
}

Добавил от себя проверку на null, о которой он там пишет в ответе

Советую перейти по ссылке и посмотреть, там много полезного написано.

READ ALSO
Dictionary ключем которого является объект

Dictionary ключем которого является объект

Допустим, имеется объект с переопределенным GetHashCode, который я хочу сделать ключемGetHashCode вычисляется, как совокупность GetHashCode всех полей

402
Нужно получить на c# исходный текст html страницы

Нужно получить на c# исходный текст html страницы

И на самом то деле задачка проста!

590
Клиент IMAP работающий через прокси

Клиент IMAP работающий через прокси

Доброго времени суток, сейчас нахожусь в поисках IMAP клиента, который бы поддерживал работу через прокси и был бесплатнымГугл достаточно...

491
Breakpoint если программа зажралась

Breakpoint если программа зажралась

Хочу поставить условный breakpoint, который сработает, если программа съела слишком много памятиПопытался прописать такое в условие:

411