c# - .NET 类应何时覆盖 Equals()?什么时候不应该?

标签 c# equals gethashcode

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/

相关文章:

c# - 自定义 NLog 过滤器类的单元测试

c# - string.GetHashCode() 在调试和发布中返回不同的值,如何避免这种情况?

java - 为什么 new String ("a") == new String ("a") 给出 false 并且 "a"= ="a"给出 true。为什么在后面的案例中它们似乎都被视为同一实例?

vb.net - 当 key 是对象时实现 containsKey

java - 为什么这两个实例不是 `equal()` ?

c# - 计算具有字符串属性的类的哈希码的最佳方法是什么?

c# - 如何在覆盖 GetHashCode() 的类型上使用 Object.GetHashCode()

c# - 检查 string[] 中的所有值的长度?

c# - 如何从UserControl1 WPF MVVM中的按钮将数据从一个UserControl1传递到UserControl2

c# - 避免 VBCSCompiler 性能命中 Roslyn 支持的 ASP.NET Razor MVC View ?