我有一些带有一堆字段的对象,我发现自己必须实现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;
}
除了速度考虑因素,为什么我不应该像这样实现它们?
答案 0 :(得分: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
答案 1 :(得分:5)
任何值类型属性都会被GetValue
调用装箱,这意味着即使它们具有相同的值,它们也永远不会相等。
您可以通过调用静态Equals(x,y)
方法来避免这种情况 - 如果需要,它将遵循虚拟x.Equals(y)
方法 - 而不是使用非虚拟==
运算符,在这种情况下,它总是测试引用相等。
if (!object.Equals(p.GetValue(obj, null), p.GetValue(this, null)))
return false;
答案 2 :(得分:2)
它可能会给出条件差的哈希(并非所有属性在确定对象标识时都相同。)
目前已实施,哈希计算可能会溢出。
答案 3 :(得分:2)
如果您的对象仅等于 ,如果所有属性相等,则继续。但我对此表示怀疑。例如,员工的员工ID是唯一的。如果这样做,您将无法比较员工数据的变化。