Расчет 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();
(Это также не гарантирует уникальность.)