Расчет GetHashCode

Я пытаюсь переопределить GetHashCode чтобы обеспечить уникальность, так как я использую экземпляры в качестве ключей в словаре:

IDictionary<Base, int> _counts = new Dictionary<Base,int>();

У меня есть две проблемы:

class sealed First : Base
{
    public MyEnum1 value;
    public ExtrasEnum extras;

    public override int GetHashCode()
    {
        unchecked
        {
            return ((int)value* 397) ^ (int)extras;
        }   
    }

    //Other stuff
}

class sealed Second : Base
{
    public MyEnum2 value;
    public ExtrasEnum extras;

    public override int GetHashCode()
    {
        unchecked
        {
            return ((int)value* 397) ^ (int)extras;
        }            
    }

    //Other stuff
}

Тем не мение. Проблема в том, что когда value а также extras Значения int становятся одинаковыми, тогда хеш-коды будут равны. Расчет был рекомендован Resharper. Как я могу убедиться, что хэш-коды для этих классов не совпадают? Просто смешайте немного с другим простым числом, или?

РЕДАКТИРОВАТЬ: просто чтобы объяснить. Мне это нужно, если в случаях First имеет то же самое value а также extras значения, то эти два экземпляра должны считаться одинаковыми, но если экземпляр Firstи экземпляр Second имеют одинаковые значения int value а также extras, то это не должно считаться одним и тем же.

Я не смотрю на производительность, а просто для того, чтобы одинаковые экземпляры классов были равны, а разные экземпляры классов различны.

2 ответа

Решение

Не очень сложно создать идеальный хеш из членов enum. Предполагая, что они не будут иметь более 256 членов, вы можете написать быстрый с:

public override int GetHashCode() {
    return ((int)value << 8) ^ (int)extras; 
}

И не генерировать никаких коллизий, написав Second.GetHashCode() как:

public override int GetHashCode() {
    return ((int)value << 16) ^ (int)extras; 
}

Очень просто и идеально, но, конечно, не масштабируется, когда вы добавляете больше производных классов. Это действительно не нужно, вы микрооптимизируете, не имея представления о том, как это действительно ускоряет ваш код. Помните, что идеальный хеш не предотвращает столкновения сегментов в словаре, индекс сегмента вычисляется путем взятия по модулю хэш-кода с простым числом. Чем больше элементов в словаре, тем больше простое число.

Просто не делай этого вообще. И всегда используйте профилировщик, если вы хотите знать, если вам нужно в любом случае.

Я предполагаю, что вы думаете, что хеш-коды не должны конфликтовать. Это явно невозможно обеспечить в целом. Следующая реализация GetHashCode всегда действует: return 0;, (Это просто медленно, но не неправильно.)

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

if (a.GetType() != b.GetType()) return false;

В случае, если я неправильно понял вашу проблему, буквальным ответом на ваш вопрос было бы указать тип класса:

oldHashCode ^ this.GetType().GetHashCode();

(Это также не гарантирует уникальность.)

Другие вопросы по тегам