在this article中,Jon Skeet提到他通常使用这种算法来覆盖 GetHashCode()。
public override int GetHashCode()
{
unchecked // Overflow is fine, just wrap
{
int hash = 17;
// Suitable nullity checks etc, of course :)
hash = hash * 23 + Id.GetHashCode();
return hash;
}
}
现在,我已经尝试过使用它了,但是Resharper告诉我,方法 GetHashCode()应该只使用只读字段进行哈希处理(尽管编译得很好)。什么是一个好的做法,因为现在我不能真正让我的领域成为只读?
我尝试通过Resharper生成此方法,这是结果。
public override int GetHashCode()
{
return base.GetHashCode();
}
老实说,这没什么贡献......
答案 0 :(得分:17)
如果您的所有字段都是可变的,并且您必须实现GetHashCode
方法,那么我担心这是您需要的实现。
public override int GetHashCode()
{
return 1;
}
是的,这是低效的,但这至少是正确的。
问题是Dictionary和HashSet集合正在使用GetHashCode
将每个项目放在一个存储桶中。如果基于某些可变字段计算哈希码,并且在将对象放入哈希集或字典后实际更改了字段,则无法再从哈希集或字典中找到该对象。
请注意,所有对象都返回相同的HashCode 1,这基本上意味着所有对象都放在HashSet或Dictionary中的同一个桶中。因此,HashSet或Dictionary中始终只有一个存储桶。尝试查找对象时,它将对唯一存储桶中的每个对象执行相等检查。这就像在链表中进行搜索一样。
有人可能会争辩说,如果我们可以确保在将对象添加到HashCode或Dictionary集合后永远不会更改字段,那么基于可变字段实现哈希码就可以了。我个人认为这很容易出错。两年后有人接管你的代码可能不会意识到这一点并且意外地破坏了代码。
答案 1 :(得分:5)
请注意,您的GetHashCode必须与您的Equals方法齐头并进。如果你可以只使用引用相等(当你的类中没有两个不同的实例可以相等时),那么你可以安全地使用继承自Object的Equals和GetHashCode。这比GetHashCode中的return 1
要好得多。
答案 2 :(得分:-1)
我个人倾向于在没有不可变字段的类中为GetHashCode()
的每个实现返回不同的数值。这意味着如果我有一个包含不同实现类型的字典,则有可能将不同类型的不同实例放在不同的存储区中。
例如
public class A
{
// TODO Equals override
public override int GetHashCode()
{
return 21313;
}
}
public class B
{
// TODO Equals override
public override int GetHashCode()
{
return 35507;
}
}
然后,如果我有一个Dictionary<object, TValue>
包含A
,B
和其他类型的实例,那么查找的效果将优于GetHashCode
的所有实现返回相同的数值。
还应该注意的是,我使用素数来获得更好的分布。
根据评论,我提供了一个LINQPad示例here,它演示了对不同类型使用return 1
和为每种类型返回不同值之间的性能差异。