Ну, есть плюсы и минусы любой реализации GetHashCode()
.Это, конечно, вещи, которые мы взвешиваем при реализации наших собственных, но в случае ValueType.GetHashCode()
есть особая трудность в том, что они не имеют большой информации о том, какими будут фактические детали конкретного типа.Конечно, это часто случается с нами, когда мы создаем абстрактный класс или класс, предназначенный для использования в качестве базы классов, которые добавят намного больше в плане состояния, но в этих случаях у нас есть очевидное решение - просто использовать реализацию по умолчанию.object.GetHashCode()
, если производный класс не захочет переопределить его там.
С ValueType.GetHashCode()
у них нет такой роскоши, как основного различия между типом значения и ссылочным типом, несмотря на популярность разговорао деталях реализации стека и кучи, тот факт, что для эквивалентности типа значения относится к значению, в то время как для эквивалентности типа объекта относится к идентичности (даже когда объект определяет другую форму эквивалентности путем переопределения Equals()
и GetHashCode()
Концепция равенства ссылок все еще существует и все еще полезна.
Итак, для метода Equals()
реализация очевидна: проверьте, что оба объекта имеют одинаковый тип, и, если это так, проверьте также, что все поляравны (на самом деле есть оптимизация, которая дв некоторых случаях выполняется побитовое сравнение, но это оптимизация той же базовой идеи).
Что делать для GetHashCode()
?Идеального решения просто не существует.Одна вещь, которую они могли бы сделать, это что-то вроде mult-then-add или shift-then-xor для каждого поля.Это, вероятно, дало бы довольно хороший хэш-код, но могло бы быть дорогим, если бы было много полей (не берите в голову, что не рекомендуется иметь типы значений, у которых есть много полей, разработчик должен учитывать, что они все еще могут, и действительномогут даже быть случаи, когда это имеет смысл, хотя я, честно говоря, не могу представить время, когда оно имеет смысл и имеет смысл его хешировать).Если бы они знали, что некоторые поля редко различались между экземплярами, они могли бы игнорировать эти поля и при этом иметь довольно хороший хэш-код, при этом будучи достаточно быстрым.Наконец, они могут игнорировать большинство полей и надеяться, что те поля, которые они не игнорируют, в большинстве случаев будут меняться.Они выбрали самую крайнюю версию последней.
(Вопрос о том, что делается, когда нет полей экземпляра, - другой вопрос, и довольно хороший выбор, такие типы значений равны всем другим экземплярамтого же типа, и у них есть хеш-код, который соответствует этому.)
Итак, это реализация, которая отстой, если вы хэшируете много значений, когда первое поле совпадает (или иначе возвращает тот же хеш-код),но другие реализации могут быть неудачными в других случаях (Mono идет за кеширование всех хеш-кодов полей вместе, лучше в вашем случае, хуже в других).
Вопрос изменения порядка полей не имеет значения, так как хеш-коддостаточно четко указано, что оно остается действительным только на протяжении всего жизненного цикла процесса и не подходит для большинства случаев, когда их можно сохранить и после этого (может быть полезно в некоторых ситуациях кэширования, где не повредит, если после неправильногоизменение кода).
Так что, не очень, но ничего не было бы идеально.Это говорит о том, что всегда нужно учитывать обе стороны, что означает «равенство» при использовании объекта в качестве ключа.Это легко исправить в вашем случае с помощью:
public class KVPCmp<TKey, TValue> : IEqualityComparer<KeyValuePair<TKey, TValue>>, IEqualityComparer
{
bool IEqualityComparer.Equals(object x, object y)
{
if(x == null)
return y == null;
if(y == null)
return false;
if(!(x is KeyValuePair<TKey, TValue>) || !(y is KeyValuePair<TKey, TValue>))
throw new ArgumentException("Comparison of KeyValuePairs only.");
return Equals((KeyValuePair<TKey, TValue>) x, (KeyValuePair<TKey, TValue>) y);
}
public bool Equals(KeyValuePair<TKey, TValue> x, KeyValuePair<TKey, TValue> y)
{
return x.Key.Equals(y.Key) && x.Value.Equals(y.Value);
}
public int GetHashCode(KeyValuePair<TKey, TValue> obj)
{
int keyHash = obj.GetHashCode();
return ((keyHash << 16) | (keyHash >> 16)) ^ obj.Value.GetHashCode();
}
public int GetHashCode(object obj)
{
if(obj == null)
return 0;
if(!(obj is KeyValuePair<TKey, TValue>))
throw new ArgumentException();
return GetHashCode((KeyValuePair<TKey, TValue>)obj);
}
}
Используйте это в качестве компаратора при создании словаря, и все должно быть хорошо (вам нужны только общие методы компаратора, но оставление в этом не вредит.и может быть полезно иметь иногда).