为什么我不应该使用反射实现Equals和GetHashCode?

时间:2010-12-07 16:47:28

标签: c# reflection

我有一些带有一堆字段的对象,我发现自己必须实现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 个答案:

答案 0 :(得分:7)

以下是我避免这条路线的几个原因

  • 比较字段而不是属性
  • 更可靠
  • 如果两个对象是相同的引用(您正在使用==),那么您的代码会假设两个对象被认为是相等的。情况并非如此,因为许多类型通过.Equals实现了值相等。将两个不同的引用视为Equals是非常可能和合法的,并且会超过您的测试。
  • 如果通过代码库以广泛的方式使用这种形式的Equality,当对象图具有循环时,它将很容易导致无限递归。
  • GetHashCode方法忽略属性可能为null

下面是一个在应用程序中导致无限递归的类型的具体示例

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

var local = new C1();
local.Prop1 = local;
var x = local.GetHashCode();  // Infinite recursion

答案 1 :(得分:5)

任何值类型属性都会被GetValue调用装箱,这意味着即使它们具有相同的值,它们也永远不会相等。

您可以通过调用静态Equals(x,y)方法来避免这种情况 - 如果需要,它将遵循虚拟x.Equals(y)方法 - 而不是使用非虚拟==运算符,在这种情况下,它总是测试引用相等。

if (!object.Equals(p.GetValue(obj, null), p.GetValue(this, null)))
    return false;

答案 2 :(得分:2)

  1. 它可能会给出条件差的哈希(并非所有属性在确定对象标识时都相同。)

  2. 目前已实施,哈希计算可能会溢出。

答案 3 :(得分:2)

如果您的对象仅等于 ,如果所有属性相等,则继续。但我对此表示怀疑。例如,员工的员工ID是唯一的。如果这样做,您将无法比较员工数据的变化。