Должен ли GetHashCode зависеть от типа? - PullRequest
3 голосов
/ 08 сентября 2011

Во-первых, я использую описанный алгоритм GetHashCode, здесь . Теперь представьте себе следующий (надуманный) пример:

class Foo
{
    public Foo(int intValue, double doubleValue)
    {
        this.IntValue = intValue;
        this.DoubleValue = doubleValue;
    }

    public int IntValue { get; private set; }
    public double DoubleValue { get; private set; }

    public override int GetHashCode()
    {
        unchecked
        {
            int hash = 17;

            hash = hash * 23 + IntValue.GetHashCode();
            hash = hash * 23 + DoubleValue.GetHashCode();
            return hash;
        }

    }
}

class DerivedFoo : Foo
{
    public DerivedFoo(int intValue, double doubleValue)
       : base(intValue, doubleValue)
    {

    }
}

Если у меня есть Foo и DerivedFoo с одинаковыми значениями для каждого из свойств, они будут иметь одинаковый хэш-код. Это означает, что я мог бы иметь HashSet<Foo> или использовать метод Distinct в Linq, и эти два экземпляра были бы обработаны, как если бы они были одинаковыми.

Я, вероятно, просто неправильно понимаю использование GetHashCode, но я ожидаю, что эти два экземпляра будут иметь разные хэш-коды. Это неверное ожидание или GetHashCode должен использовать тип в расчете? (Или DerivedClass также переопределить GetHashCode)?

P.S. Я понимаю, что есть много, много вопросов по SO, относящихся к этой теме, но я не нашел ни одного, который бы прямо отвечал на этот вопрос.

Ответы [ 2 ]

6 голосов
/ 08 сентября 2011

GetHashCode() не должен гарантировать уникальность (хотя это помогает для производительности, если она настолько уникальна, насколько это возможно).

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

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

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

Теперь, учитывая приведенный выше пример, вы не реализуете Equals(), в результате каждый объект будет отличаться независимо от хеш-кода, поскольку реализация по умолчанию Equals() из object является проверкой равенства ссылок.

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

  • Объекты, которые Equals() должны , имеют одинаковые GetHashCode() результаты.
  • Объекты с разными GetHashCode() должны не быть Equals().
1 голос
/ 08 сентября 2011

Два экземпляра не должны иметь разные хеш-коды. Результаты GetHashCode не предполагаются HashSet или другими классами каркаса, потому что могут быть конфликты даже внутри типа. GetHashCode просто используется для определения местоположения в хеш-таблице для хранения элемента. Если в HashSet есть коллизия, он прибегает к результату метода Equals для определения уникального соответствия. Это означает, что когда бы вы ни внедрили GetHashCode, вы должны также реализовать Equals (и проверить, совпадают ли типы). Аналогично, всякий раз, когда вы реализуете Equals, вы также должны реализовывать GetHashCode. Смотрите хорошее объяснение Эрика Липперта здесь .

...