2010-12-07 2 views
9

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

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; 
} 

Помимо соображений скорости, почему я не должен выполнять их, как это?

+0

Помимо вопросов скорости, обратите внимание, что не все правильные реализации «GetHashCode» и «Equals» эквивалентны вышеуказанному алгоритму. Кстати, есть несколько проблем с опубликованным кодом. Вы можете разыменовать «null» в нескольких местах. Кроме того, ваша версия `Equals` использует ссылочное равенство между соответствующими свойствами, которое не является наиболее распространенной идиомой. – Ani 2010-12-07 16:53:05

+0

Забавно, что вы должны спросить, это так. – stimms 2010-12-07 16:53:42

+0

Используйте ReSharper - он будет генерировать правильные реализации `Equals` и` GetHashCode` для вас. – 2010-12-07 16:54:58

ответ

7

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

  • Это гораздо надежнее сравнивать поля вместо свойств
  • Ваш код делает неверное предположение, что два объекта считаются равными, если они та же ссылка (вы используете ==). Это не так, поскольку многие типы реализуют равенство значений через .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

Любые свойства типа значений будут помещаться в очередь по звонкам GetValue, а это означает, что они никогда не будут сравниваться как равные, даже если они имеют одинаковое значение.

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

if (!object.Equals(p.GetValue(obj, null), p.GetValue(this, null))) 
    return false; 
2
  1. Это может дать плохо условный хэш (не все свойства равны в определении идентичности объекта.)

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

2

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

Смежные вопросы