Упрощение переопределения Equals (), GetHashCode () в C # для лучшей управляемости - PullRequest
9 голосов
/ 14 марта 2012

Я часто переопределяю Equals() и GetHashCode(), чтобы реализовать семантику, согласно которой бизнес-объекты с одинаковыми значениями свойств равны.Это приводит к тому, что код является повторяющимся для написания и хрупким для поддержки (свойство добавляется, и одно / оба переопределения не обновляются).

В конечном итоге код выглядит примерно так (комментарии к реализации приветствуются):

public override bool Equals(object obj)
{
    if (object.ReferenceEquals(this, obj)) return true;

    MyDerived other = obj as MyDerived;

    if (other == null) return false;

    bool baseEquals = base.Equals((MyBase)other);
    return (baseEquals && 
        this.MyIntProp == other.MyIntProp && 
        this.MyStringProp == other.MyStringProp && 
        this.MyCollectionProp.IsEquivalentTo(other.MyCollectionProp) && // See http://stackoverflow.com/a/9658866/141172
        this.MyContainedClass.Equals(other.MyContainedClass));
}

public override int GetHashCode()
{
    int hashOfMyCollectionProp = 0;
    // http://computinglife.wordpress.com/2008/11/20/why-do-hash-functions-use-prime-numbers/
    // BUT... is it worth the extra math given that elem.GetHashCode() should be well-distributed?
    int bitSpreader = 31; 
    foreach (var elem in MyCollectionProp)
    {
        hashOfMyCollectionProp = spreader * elem.GetHashCode();
        bitSpreader *= 31;
    }
    return base.GetHashCode() ^ // ^ is a good combiner IF the combined values are well distributed
        MyIntProp.GetHashCode() ^ 
        (MyStringProp == null ? 0 : MyStringProp.GetHashValue()) ^
        (MyContainedClass == null ? 0 : MyContainedClass.GetHashValue()) ^
        hashOfMyCollectionProp;
}

Мои вопросы

  1. Является ли шаблон реализации надежным?
  2. Является ли ^ адекватным, учитывая, что составляющие значения компонентахорошо распределены?Нужно ли умножать на 31-на-N при объединении элементов коллекции, учитывая, что их хэш хорошо распределен?
  3. Кажется, что этот код можно абстрагировать в код, который использует отражение для определения открытых свойств, создает выражениедерево, которое соответствует решению, закодированному вручную, и выполняет дерево выражений по мере необходимости.Этот подход кажется разумным?Есть ли где-нибудь существующая реализация?

Ответы [ 3 ]

4 голосов
/ 17 марта 2012

MSDN на самом деле не говорит "не перегружайте Equals et al для изменяемых типов".Раньше это говорилось, но теперь говорится:

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

http://msdn.microsoft.com/en-us/library/dd183755.aspx

Тем не менее, существуют сложности, связанные со стабильностью хеш-кода, когда объект участвует в хешированном наборе (Dictionary<T,U>, HashSet<T> и т. Д.).

Я решил выбратьлучшее из обоих миров, как указано здесь:

https://stackoverflow.com/a/9752155/141172

1 голос
/ 14 марта 2012

Я часто нахожу свои переопределенные функции Equals () и GetHashCode ()

  • MSDN говорит: не перегружайте Equals и другие для изменяемых типов

Является ли ^ адекватным, учитывая, что значения составляющих компонентов хорошо распределены?

  • Да, но они не всегда хорошо распределены.Рассмотрим int свойства.Рекомендуется переходить на некоторые (маленькие) простые числа.
0 голосов
/ 11 апреля 2013

Возможно, я здесь запутался, но разве проверка null не должна возвращать 1 вместо 0 в переопределении GetHashCode?

So

MyStringProp == null ? 0 : MyStringProp.GetHashValue()

должно быть

MyStringProp == null ? 1 : MyStringProp.GetHashValue()
...