Equals 和 GetHashCode 的最佳策略是什么?
我正在使用域模型,并正在考虑在 .NET 中实现这两种方法的各种方法。您的首选策略是什么?
这是我当前的实现:
public override bool Equals(object obj)
{
var newObj = obj as MyClass;
if (null != newObj)
{
return this.GetHashCode() == newObj.GetHashCode();
}
else
{
return base.Equals(obj);
}
}
// Since this is an entity I can use its Id
// When I don't have an Id, I usually make a composite key of the properties
public override int GetHashCode()
{
return String.Format("MyClass{0}", this.Id.ToString()).GetHashCode();
}
I'm working with a domain model and was thinking about the various ways that we have to implement these two methods in .NET. What is your preferred strategy?
This is my current implementation:
public override bool Equals(object obj)
{
var newObj = obj as MyClass;
if (null != newObj)
{
return this.GetHashCode() == newObj.GetHashCode();
}
else
{
return base.Equals(obj);
}
}
// Since this is an entity I can use its Id
// When I don't have an Id, I usually make a composite key of the properties
public override int GetHashCode()
{
return String.Format("MyClass{0}", this.Id.ToString()).GetHashCode();
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
领域驱动设计区分了实体和值对象。这是一个值得注意的区别,因为它指导您如何实现 Equals。
实体如果 ID 相同,则它们相等。
如果值对象的所有(重要)组成元素都彼此相等,则它们是相等的。
无论如何,GetHashCode 的实现应该基于用于确定相等性的相同值。换句话说,对于实体,哈希码应该直接从 ID 计算,而对于值对象,它应该从所有组成值计算。
Domain-Driven Design makes the distinction between Entities and Value Objects. This is a good distinction to observe since it guides how you implement Equals.
Entities are equal if their IDs equal each other.
Value Objects are equal if all their (important) constituent elements are equal to each other.
In any case, the implementation of GetHashCode should base itself on the same values that are used to determine equality. In other words, for Entities, the hash code should be calculated directly from the ID, whereas for Value Objects it should be calculated from all the constituent values.
这里的答案都没有真正适合我。既然您已经说过不能使用
Id
来实现相等,并且需要使用一组属性,那么这里有一个更好的方法来做到这一点。注意:我不认为这总体上是实现Equals
和GetHashCode
的最佳方式。这是 OP 代码的更好版本。请参阅Jon Skeet 的回答了解其背后的一些原因。使用异或并不好,因为不同的数据集最终可能会产生相同的哈希值。这种使用素数(上面的种子值 19 和 31,或您选择的其他值)的环绕方法可以更好地分割成每个碰撞很少的“桶”。
如果您的任何值可以为空,我鼓励您仔细考虑它们应该如何比较。您也许可以使用短路空评估和空合并运算符。但请确保如果 null 应该比较为相等,则当它们为 null 时,您将不同的哈希代码分配给不同的可空属性。
另外,我不相信您的
Equals
实现有任何意义。当比较两个对象是否相等时,首先比较它们的 GetHashCode 值。只有当这些不同时,Equals
方法才会运行(这样,如果散列到相同值的两个对象不同,就会检测到这一点)。由于您的GetHashCode
实现不引用base
,因此您的Equals
方法这样做可能没有意义。具体来说,如果Equals
对于散列码不同的两个对象返回 true,那么您将遇到一个严重的错误等待破坏。None of the answers here really hit the spot for me. Since you already said that you can't use
Id
for equality, and you need to use a bundle of properties, here's a better way to do that. Note: I do not consider this overall to be the best way to implementEquals
andGetHashCode
. This is a better version of the OP's code.See this answer by Jon Skeet for some of the reasoning behind this. Using xor is not good because various sets of data can end up resulting in the same hash. This wrap-around method with primes (the seed values of 19 and 31 above, or other values that you choose) does a better job of segmenting into "buckets" that have few collisions each.
If any of your values can be null, I encourage you to think carefully about how they should compare. You could use short circuit null evaluation and the null coalescing operator perhaps. But make sure that if nulls should compare as equal that you assign different hash codes to the different nullable properties when they are null.
Also, I'm not convinced that your
Equals
implementation makes any sense. When two objects are compared for equality, first theirGetHashCode
values are compared. Only if those are different is theEquals
method run (so that if two objects that hash to the same value are different, this will be detected). Since yourGetHashCode
implementation doesn't refer to thebase
, it may make no sense for yourEquals
method to do so. Specifically, you will have a serious bug waiting to break things ifEquals
can return true for two objects whose hash codes are different.由于哈希码相等而假设实例相等是错误的。
我猜你的 GetHashCode 实现是可以的,但我通常使用类似的东西:
Assuming that the instances are equal because the hash codes are equal is wrong.
I guess your implementation of GetHashCode is OK, but I usually use things similar to this:
我偶然发现了这个老问题,恕我直言,我没有找到任何清晰简单的答案来说明@tucaz 提出的原始问题。
我可以同意上面(或下面:D)分享的许多考虑因素,但遗漏了“问题点”(我认为)。
前提是:
我可以猜测一个简单的实现可能是:
仅此而已!
I stumbled upon this old question and, IMHO, I didn't find any answer clear and simple stated the original question formulated by @tucaz.
I can agree with many considerations shared above (or below :D) but the «Question point» was missed (I think).
Provided that:
I can guess that one straightforward implementation could be:
That's all!
哈希码可能会发生冲突,因此我认为它们不是比较相等性的好方法。您应该比较使对象“相等”的基础值。请参阅@Jon Skeet对此问题的回答:什么是重写 System.Object.GetHashCode 的最佳算法吗? 如果您的等式包含多个属性,则可以更好地实现 GetHashCode。如果它只是一个属性,您可以重用它的哈希码。
Hashcodes can collide so I don't think they are a good way to compare equality. You should compare the underlying values that make the objects "equal" instead. See @Jon Skeet's answer to this question: What is the best algorithm for an overridden System.Object.GetHashCode? for a better GetHashCode implementation if your equality encompasses several properties. If it's just a single property, you can just reuse it's hashcode.
除了答案(我不允许写评论)之外,我想指出 Visual Studio 可以自动生成 Equals 和 GetHashCode。
请参阅此答案: Visual Studio中有没有办法自动生成equals和hashcode方法
我确实在寻找自定义实现,但在这里没有找到。
我还想链接这个问题:
比较两个复杂对象的最佳方法
它与嵌套类结构有关。在注释中,我们可以找到使用枚举(例如使用 List )的嵌套类结构的情况。
In addition to the answers (I am not allowed to write comments) I would like to point out that Visual Studio can autogenerate Equals and GetHashCode.
See this answer: Is there a way to automatically generate equals and hashcode method in Visual Studio
I was really looking for that custom implementation and didnt find it here.
I woulld also like to link this question:
Best way to compare two complex objects
It is about having a nested class structure. Down in the comments one can find the case for nested class structures with Enumerations (for example with List ).
我想根据上面的答案和我自己的经验来看看一些具体的场景。
经验法则是,具有不同哈希码的两个实例应该始终不相等,但如果它们具有相同的哈希码,它们可能相等也可能不相等。
GetHashCode()
用于快速区分实例,Equals()
用于验证相等性(无论这对您意味着什么)。此外,许多内置机制都会寻找
IEquatable
的实现,因此最好声明一个Equals(MyClass)
的重写,它实际上会执行以下操作:检查。具有唯一 ID 的类
考虑一个具有唯一 ID 的类。那么 equals 操作只会检查 id。哈希值也是如此,它仅依赖于 id。
具有属性的类
这种情况与上面类似,但是比较依赖于两个或多个属性,并且需要在哈希码中不对称地组合。这在下一个场景中将变得更加明显,但其想法是,如果一个属性具有哈希
A
且另一个属性具有哈希B
,则结果应该与第一个属性的情况不同属性具有哈希B
和另一个哈希A
。基于值的类(结构)
这与上面的情况几乎相同,除了作为值类型(
struct
声明)还需要重新定义==
和!=
调用 equals。请注意,
struct
应该是不可变的,最好在声明中添加readonly
关键字I want to look at some specific scenarios based on the answers above, and my own experiences.
A rule of thumb is, two instances with different hash codes should always be not equal, but if the have the same hash code they might or might not be equals.
GetHashCode()
is used to differentiate between instances quickly, andEquals()
is used to verify equality (whatever that means to you).Also a lot of built-in mechanisms look for an implementation of
IEquatable<T>
so it is a good idea to declare an override ofEquals(MyClass)
that actually does the checking.Class with unique ID
Consider a class with a unique ID. Then the equals operation would just check the id. The same with the hash, which solely relies on the id.
Class with properties
This case is similar to the above, but the comparisons depend on two or more properties, and the need to be combined asymmetrically in the hash code. This will become more evident in the next scenario, but the idea is if one property has hash
A
and the other property hashB
, the result should difference from the case where first property has hashB
and the other hashA
.Value based class (structure)
This is almost identical to the case above, except being a value type (
struct
declaration) requires also re-definition of==
and!=
to call equals.Note that
struct
should be immutable, and it is a good idea to add thereadonly
keyword in the declaration