我正在开发一个C#项目,直到现在,我已经使用了不可变对象和工厂来确保Foo
始终可以比较类型对象的相等性==
.
Foo
创建后无法更改对象,并且工厂始终为给定的参数集返回相同的对象.这很好用,在整个代码库中我们假设它==
始终用于检查相等性.
现在我需要添加一些引入边缘情况的功能,但这并不总是有效.最简单的方法是operator ==
为该类型重载,以便项目中的其他代码都不需要更改.但这让我感觉像代码味道:重载operator ==
并且Equals
不仅仅是看起来很奇怪,而且我习惯于==
检查引用相等性的约定,并Equals
检查对象的相等性(或任何术语).
这是一个合理的问题,还是我应该继续超载operator ==
?
重载 ==
和覆盖 Equals 之间存在很大差异.
当你有表达式
if (x == y) {
将用于比较变量x和y的方法在编译时决定.这是运算符重载.声明x和y时使用的类型用于定义用于比较它们的方法.x和y中的实际类型(即子类或接口实现)是无关紧要的.考虑以下.
object x = "hello"; object y = 'h' + "ello"; // ensure it's a different reference if (x == y) { // evaluates to FALSE
和以下
string x = "hello"; string y = 'h' + "ello"; // ensure it's a different reference if (x == y) { // evaluates to TRUE
这表明用于声明变量x和y的类型用于确定用于评估==的方法.
相比之下,Equals是在运行时根据变量x中的实际类型确定的.Equals是Object上的一个虚方法,其他类型可以并且确实覆盖它.因此,以下两个例子都评估为真.
object x = "hello"; object y = 'h' + "ello"; // ensure it's a different reference if (x.Equals(y)) { // evaluates to TRUE
和以下
string x = "hello"; string y = 'h' + "ello"; // ensure it's a different reference if (x.Equals(y)) { // also evaluates to TRUE
我相信标准是对于大多数类型,.Equals检查对象相似性,并且运算符==
检查引用相等性.
我认为最佳实践是,对于不可变类型,运算符==
应该检查相似性,以及.Equals
.如果你想知道它们是否真的是同一个对象,请使用.ReferenceEquals
.有关此String
示例,请参阅C#类.
对于不可变类型,我认为==
重载以支持值相等没有任何问题.我不认为我会覆盖==
而不会覆盖Equals
具有相同的语义.如果==
由于某种原因确实覆盖并需要检查引用相等性,则可以使用Object.ReferenceEquals(a,b)
.
有关一些有用的指导,请参阅此Microsoft文章
它绝对有气味.超载时==
,你应该确保这两个Equals()
和GetHashCode()
也是一致的.请参阅MSDN准则.
而这似乎没问题的唯一原因是你将你的类型描述为不可变的.
示例显示如何根据MSFT指南(如下)实现此目的.注意,当覆盖Equals时,您还需要覆盖GetHashCode().希望这有助于人们.
public class Person { public Guid Id { get; private set; } public Person(Guid id) { Id = id; } public Person() { Id = System.Guid.NewGuid(); } public static bool operator ==(Person p1, Person p2) { bool rc; if (System.Object.ReferenceEquals(p1, p2)) { rc = true; } else if (((object)p1 == null) || ((object)p2 == null)) { rc = false; } else { rc = (p1.Id.CompareTo(p2.Id) == 0); } return rc; } public static bool operator !=(Person p1, Person p2) { return !(p1 == p2); } public override bool Equals(object obj) { bool rc = false; if (obj is Person) { Person p2 = obj as Person; rc = (this == p2); } return rc; } public override int GetHashCode() { return Id.GetHashCode(); } }
根据Microsoft自己的最佳实践,Equals方法的结果和equals(==)重载应相同。
CA2224:重载等于运算符等于