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

C# 对于值对象与实体,等于覆盖和==重载,c#,domain-driven-design,equality,C#,Domain Driven Design,Equality,我发现了很多关于Equals重写和==运算符重载之间差异的讨论,但是对于何时应该或不应该重写默认的equality行为,似乎有一些退步,这让我怀疑这样做的好方法 以下是我的理解,如果你觉得有什么不对劲,请告诉我: 1) ==对于非不可变类型,不建议使用重载(为什么??),对于不可变类型(也称为DDD的值对象),如果值相同但引用不同,则可以使用重载使==Comparison返回true 2) Equals(和GetHashCode)也应该在不可变类型中被重写,以便对类型中相关字段的每个值执行良好的

我发现了很多关于Equals重写和==运算符重载之间差异的讨论,但是对于何时应该或不应该重写默认的equality行为,似乎有一些退步,这让我怀疑这样做的好方法

以下是我的理解,如果你觉得有什么不对劲,请告诉我:

1) ==对于非不可变类型,不建议使用重载(为什么??),对于不可变类型(也称为DDD的值对象),如果值相同但引用不同,则可以使用重载使==Comparison返回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);

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

何时使用 为了不产生笨拙的行为,只对不可变类型实现相等操作。这一点很重要,因为例如,哈希映射不能正确地处理可变类型(请考虑当对象的哈希代码在哈希映射中更改时会发生什么情况)

单独实现
Equals
对于某些可变类型可能是有意义的,但通常不鼓励这样做,例如

价值对象 相等操作在值对象上最有用。还覆盖相等运算符,使相等比较看起来自然

相等操作的实现是微不足道的:考虑所有数据字段,但忽略计算属性。这将创建纯粹基于内容的相等操作

因为在值对象上实现相等操作是机械的,所以有一种方法可以自动为您实现这一点(这是我自己编写的)。它将在静态实例化时创建与手动编写的
Equals
GetHashCode
实现具有相同运行时性能的相等操作

实体 对于实体,它变得有点棘手。问题是,从领域的角度来看,平等的含义通常并不明确

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


好消息是,实际上并不需要中的这种功能。因此,我的建议是:不要在实体上实现相等操作。

在处理集合时,相等问题是最重要的。很好的解释,谢谢,我说的对吗?如果我们使用结构c,它将为我们做这项工作,并自动执行值相等,因此我们不需要实现它。此外,到目前为止(但我不是太远;)我发现ddd值对象似乎很自然地适合c#struct实现,对每个值对象使用struct是一个好主意吗?对于复杂的对象可能有一些例外……结构只适用于最小的值对象。我通常将VOs设计为类,除非我有非常具体的理由不这样做。
Assert.Equal(expectedAddress, address);