等于覆盖和 == 重载,对于值对象与实体

Equals override and == overload, for value objects vs entities

我发现很多关于 Equals 覆盖和 == 运算符重载之间的区别的讨论,但是对于我们何时应该或不覆盖默认的相等行为似乎存在一些分歧,这让我怀疑这样做的好方法它。

以下是我的理解,如有不妥之处请指正:

1) 不建议对非不可变类型使用 == 重载(为什么??),并且对于不可变类型(又名 DDD 的值对象)很有用,使得 == 比较 return true 如果值相同但参考文献不同。

2) 在不可变类型中也应覆盖 Equals(和 GetHashCode),以便对类型内相关字段的每个值进行良好比较。

3) 实体的 Equals 怎么样?

覆盖 Equals 并只比较 id 属性是个好主意吗?或者我应该让比较引用的默认对象行为?

对于这两个选项,我认为如果我遵循一条规则,即在线程上下文中我应该始终只有一个特定实体的实例,结果应该是相同的,但是有什么缺点或优点吗我应该注意这些选项之一?

动机

在我看来,正确的相等操作是面向对象世界中最被低估的工具之一。是的,你绝对应该在有意义的地方实现它们,它会让你的程序更加简洁。

例如比较

Assert.Equal(expectedAddress.Street, address.Street);
Assert.Equal(expectedAddress.City, address.City);
Assert.Equal(expectedAddress.Zip, address.Zip);
Assert.Equal(expectedAddress.State, address.State);
Assert.Equal(expectedAddress.Country, address.Country);

Assert.Equal(expectedAddress, address);

当你有深度嵌套的值对象时,这会变得更加极端。

何时使用

为了不产生尴尬的行为,只对不可变类型实现相等操作。这很重要,因为例如哈希映射将无法与可变类型一起正常工作(想想当对象的哈希码在哈希映射中发生变化时会发生什么)。

单独实现 Equals 对于某些可变类型可能有意义,但通常不鼓励,例如通过 Microsoft code analysis rule

值对象

相等运算对值对象最有用。还重写相等运算符以使相等比较看起来自然。

相等运算的实现很简单:考虑所有数据字段但忽略计算属性。这将创建纯粹基于内容的相等操作。

由于在值对象上实现相等操作是机械的,因此有一个 library called Equ 可以自动为您执行此操作(这是我自己编写的)。它将在静态实例化时创建相等操作,与手动编写的 EqualsGetHashCode 实现具有相同的运行时性能。

实体

对于实体,它变得有点棘手。问题是从领域的角度.

,通常并不清楚相等的含义。

显然,两个 Customer 个 ID 不同的实体是不相等的。但仅此而已。具有相同 ID 但处于不同状态的两个 Customer 实体是否相等?一道难题。

好消息是这种功能并不是真正需要的。所以我的建议是:不要对实体实施相等操作。