Почему я не должен реализовывать Equals и GetHashCode, используя отражение? - PullRequest
9 голосов
/ 07 декабря 2010

У меня есть несколько объектов с кучей полей, и мне приходится реализовывать GetHashCode и Equals.Больно обходить каждое поле вручную, поэтому я написал их так:

public override int GetHashCode()
{
    int hash = 17;
    foreach (PropertyInfo p in GetType().GetProperties())
    {
        hash = hash * 23 + p.GetValue(this, null).GetHashCode();
    }
    return hash;
}

public override bool Equals(object obj)
{
    foreach (PropertyInfo p in GetType().GetProperties())
    {
        if (p.GetValue(obj, null) != p.GetValue(this, null))
            return false;
    }
    return true;
}

Кроме соображений скорости, почему бы мне не реализовать их так?

Ответы [ 4 ]

7 голосов
/ 07 декабря 2010

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

  • Гораздо надежнее сравнивать поля, а не свойства
  • Ваш код неверно полагает, что два объекта считаются равными, если они имеют одинаковую ссылку (вы используете ==). Это не так, поскольку многие типы реализуют равенство значений через .Equals. Вполне возможно и законно, чтобы две разные ссылки считались Equals и прошли бы ваш тест.
  • Если эта форма равенства будет широко использоваться в вашей кодовой базе, это очень легко приведет к бесконечной рекурсии, когда у графа объекта есть циклы.
  • Метод GetHashCode игнорирует, что свойство может быть null

Ниже приведен конкретный пример типа, который может вызвать бесконечную рекурсию в вашем приложении

class C1 {
  public object Prop1 { get; set; }
};

var local = new C1();
local.Prop1 = local;
var x = local.GetHashCode();  // Infinite recursion
5 голосов
/ 07 декабря 2010

Любые свойства типа значения будут упакованы в вызовы GetValue, что означает, что они никогда не будут сравниваться как равные, даже если они имеют одинаковое значение.Equals(x,y) метод - который затем будет при необходимости использовать виртуальный x.Equals(y) метод - вместо использования не виртуального оператора ==, который всегда будет проверять ссылкуравенство в этом случае.

if (!object.Equals(p.GetValue(obj, null), p.GetValue(this, null)))
    return false;
2 голосов
/ 07 декабря 2010

Если ваш объект равен только , если все свойства равны, тогда продолжайте. Но я сомневаюсь в этом. Например, сотрудник уникален своим идентификатором сотрудника. Если вы сделаете это, вы не сможете сравнивать изменения в данных о сотрудниках.

2 голосов
/ 07 декабря 2010
  1. Может давать плохо обусловленный хэш (не все свойства одинаковы при определении идентичности объекта.)

  2. Как и в настоящее время реализовано, вычисление хеша может переполниться.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...