动机
在我看来,正确的相等运算是面向对象世界中最被低估的工具之一。是的,您绝对应该在有意义的地方实现它们,这将使您的程序更加简洁。
例如比较
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 会自动为您执行此操作(这是我自己编写的)。它将在静态实例化时创建相等操作,其运行时性能与手动编写的 Equals 和 GetHashCode 实现相同。
实体
对于实体,它变得有点棘手。问题在于,从领域的角度来看,平等的含义通常不是很清楚。
显然,两个 ID 不同的Customer 实体并不相等。但仅此而已。具有相同 ID 但处于不同状态的两个 Customer 实体是否相等?难题。
好消息是这种功能并不是真正需要的。所以我的建议是:不要对实体执行相等操作。