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

sti*_*mms 9 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;
}
Run Code Online (Sandbox Code Playgroud)

除了速度考虑因素,为什么我不应该像这样实现它们?

Jar*_*Par 8

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

  • 比较字段而不是属性更可靠
  • 如果两个对象是相同的引用(您正在使用==),那么您的代码会假设两个对象被认为是相等的.情况并非如此,因为许多类型通过实现值相等.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
Run Code Online (Sandbox Code Playgroud)


Luk*_*keH 5

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

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

if (!object.Equals(p.GetValue(obj, null), p.GetValue(this, null)))
    return false;
Run Code Online (Sandbox Code Playgroud)