VS2005 文档 Guidelines for Overloading Equals() and Operator == (C# Programming Guide)部分国家
Overriding operator == in non-immutable types is not recommended.
较新的 .NET Framework 4 文档 Guidelines for Implementing Equals and the Equality Operator (==)省略了该声明,尽管社区内容中的一篇帖子重复了该声明并引用了旧文档。
至少对于一些微不足道的可变类来说,覆盖 Equals() 似乎是合理的,例如
public class ImaginaryNumber
{
public double RealPart { get; set; }
public double ImaginaryPart { get; set; }
}
在数学中,实部和虚部相同的两个虚数在测试相等性的时间点实际上是相等的。断言它们不相等是不正确的,如果不覆盖具有相同 RealPart 和 ImaginaryPart 的单独对象 Equals(),就会发生这种情况。
另一方面,如果一个人覆盖了 Equals(),那么也应该覆盖 GetHashCode()。如果将覆盖 Equals() 和 GetHashCode() 的 ImaginaryNumber 置于 HashSet 中,并且可变实例更改其值,则该对象将不再在 HashSet 中找到。
MSDN 是否错误地删除了关于不覆盖非不可变类型的 Equals()
和 operator==
的指南?
对于可变类型覆盖 Equals() 是否合理,其中“在现实世界中”所有属性的等价性意味着对象本身是相等的(与 ImaginaryNumber
一样)?
如果合理,当对象实例参与 HashSet 或其他依赖于 GetHashCode() 不变的东西时,如何最好地处理潜在的可变性?
更新
刚刚发现这个in MSDN
Typically, you implement value equality when objects of the type are expected to be added to a collection of some sort, or when their primary purpose is to store a set of fields or properties. You can base your definition of value equality on a comparison of all the fields and properties in the type, or you can base the definition on a subset. But in either case, and in both classes and structs, your implementation should follow the five guarantees of equivalence:
最佳答案
我开始意识到,我希望 Equals 表示两种不同的意思,具体取决于上下文。在这里权衡输入后以及here ,我已针对我的具体情况确定了以下内容:
我没有覆盖 Equals()
和 GetHashCode()
,而是保留常见但绝非无处不在的约定 Equals()
意味着类的身份平等,并且 Equals()
表示结构的值相等。这个决定的最大驱动因素是散列集合中对象的行为( Dictionary<T,U>
, HashSet<T>
,...)如果我偏离这个约定。
那个决定让我仍然缺少值(value)平等的概念(如 discussed on MSDN )
When you define a class or struct, you decide whether it makes sense to create a custom definition of value equality (or equivalence) for the type. Typically, you implement value equality when objects of the type are expected to be added to a collection of some sort, or when their primary purpose is to store a set of fields or properties.
需要值相等(或我称之为“等价”)概念的典型案例是在单元测试中。
给定
public class A
{
int P1 { get; set; }
int P2 { get; set; }
}
[TestMethod()]
public void ATest()
{
A expected = new A() {42, 99};
A actual = SomeMethodThatReturnsAnA();
Assert.AreEqual(expected, actual);
}
测试将失败,因为 Equals()
正在测试引用相等性。
当然可以修改单元测试以单独测试每个属性,但这会将等价概念从类中移到类的测试代码中。
为了将这些知识封装在类中,并为测试等价性提供一致的框架,我定义了一个我的对象实现的接口(interface)
public interface IEquivalence<T>
{
bool IsEquivalentTo(T other);
}
实现通常遵循以下模式:
public bool IsEquivalentTo(A other)
{
if (object.ReferenceEquals(this, other)) return true;
if (other == null) return false;
bool baseEquivalent = base.IsEquivalentTo((SBase)other);
return (baseEquivalent && this.P1 == other.P1 && this.P2 == other.P2);
}
当然,如果我有足够的类和足够的属性,我可以编写一个帮助器,通过反射构建一个表达式树来实现 IsEquivalentTo()
.
最后,我实现了一个扩展方法来测试两个 IEnumerable<T>
的等价性:
static public bool IsEquivalentTo<T>
(this IEnumerable<T> first, IEnumerable<T> second)
如果T
工具 IEquivalence<T>
使用该接口(interface),否则 Equals()
用于比较序列的元素。允许回退到 Equals()
让它工作,例如与 ObservableCollection<string>
除了我的业务对象。
现在,我的单元测试中的断言是
Assert.IsTrue(expected.IsEquivalentTo(actual));
关于c# - .NET 类应何时覆盖 Equals()?什么时候不应该?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9709088/