реализация GetHashCode () для объектов, содержащих коллекции
рассмотрим следующие объекты:
class Route
{
public int Origin { get; set; }
public int Destination { get; set; }
}
маршрут реализует операторы равенства.
class Routing
{
public List<Route> Paths { get; set; }
}
я использовал приведенный ниже код для реализации метода GetHashCode для объекта маршрутизации, и он, похоже, работает, но мне интересно, правильно ли это сделать? Я полагаюсь на проверки на равенство и, поскольку я не уверен, я подумал, что спрошу вас, ребята. Могу ли я просто суммировать хэш-коды или мне нужно сделать больше магии, чтобы гарантировать желаемый эффект?
public override int GetHashCode() =>
{
return (Paths != null
? (Paths.Select(p => p.GetHashCode())
.Sum())
: 0);
}
Я проверил несколько GetHashCode()
вопросы здесь, а также MSDN и статья Эрика Липперта по этой теме, но не смогли найти то, что я ищу.
4 ответов
Я думаю, что ваше решение-это хорошо. (Гораздо позже замечание: для LINQ-х Sum
метод будет действовать в checked
контекст, поэтому вы можете очень легко получить OverflowException
что означает, что это не так хорошо, в конце концов.) Но более привычно делать XOR (дополнение без переноски). Так что это может быть что-то вроде
public override int GetHashCode()
{
int hc = 0;
if (Paths != null)
foreach (var p in Paths)
hc ^= p.GetHashCode();
return hc;
}
добавление (после того, как ответ был принят):
помните, что если вы когда-либо использовать этот тип Routing
на Dictionary<Routing, Whatever>
, a HashSet<Routing>
или другая ситуация, когда хэш используется таблица, тогда ваш экземпляр будет проиграл если кто-то изменяет (мутирует) в Routing
после добавления в коллекцию.
если вы уверены, что никогда не произойдет, используйте мой код выше. Dictionary<,>
и так далее будет работать, если вы убедитесь, что никто не изменяет Routing
это ссылка.
другой вариант-просто написать
public override int GetHashCode()
{
return 0;
}
если вы считаете, что хэш-код не будет использоваться. Если каждый instace возвращает 0
для окрошки код, вы получите очень плохой работы с хэш-таблицами, но ваш объект не будет потерян. Третий вариант-бросить NotSupportedException
.
код из ответа Jeppe Stig Nielsen работает, но это может привести к большому количеству повторяющихся значений хэш-кода. Предположим, вы хэшируете список ints в диапазоне 0-100, тогда ваш хэш-код будет защищен от 0 до 255. Это приводит к большим коллизиям при использовании в словаре. Вот улучшенная версия:
public override int GetHashCode()
{
int hc = 0;
if (Paths != null)
foreach (var p in Paths) {
hc ^= p.GetHashCode();
hc = (hc << 7) | (hc >> (32 - 7)); //rotale hc to the left to swipe over all bits
}
return hc;
}
этот код, по крайней мере, будет включать все биты с течением времени, поскольку все больше и больше элементов хэшируются.
в качестве ориентира хэш объекта должен быть одинаковым на протяжении всего срока службы объекта. Я бы оставил GetHashCode
функция в одиночку, а не перезаписать его. Хэш-код используется только если вы хотите поместить свои объекты в хэш-таблице.
вы должны прочитать отличную статью Эрика Липперта о хэш-кодах в .NET:руководство и правила для GetHashCode.
цитата из этой статьи:
Guideline: целое число, возвращаемое GetHashCode никогда не должен меняться
правило: целое число, возвращаемое GetHashCode, никогда не должно изменяться, пока объект содержится в структуре данных, которая зависит от хэш-кода, остающегося стабильным
если хэш-код объекта может мутировать, пока он находится в хэш-таблице, то ясно, что метод Contains перестает работать. Вы помещаете объект в ведро #5, мутируете его, и когда вы спрашиваете набор, содержит ли он мутированный объект, он смотрит в ведро #74 и не находит он.
на GetHashCode
реализованная вами функция не будет возвращать один и тот же хэш-код в течение всего срока службы объекта. Если вы используете эту функцию, у вас возникнут проблемы, если вы добавите эти объекты в хэш-таблицу:на Contains
метод не будет работать.
Я не думаю, что это правильный способ сделать, потому что dtermine окончательный hashcode
Он должен быть уникальным для указанного объекта. В вашем случае вы делаете Sum()
, который может произвести то же самое результат с разными хэш-кодами в коллекции (в конце хэш-коды-это просто целые числа).
Если вы намерены определить равенство на основе содержимого коллекции, на данный момент просто сравните эти cillections между двумя объектами. Это может отнимает много времени операция, кстати.