【问题标题】:Why is my implementation of Equals() not called by AreEqual()为什么 AreEqual() 不调用我的 Equals() 实现
【发布时间】:2013-03-06 15:48:08
【问题描述】:

如果我想根据字段值而不是参考来检查相等性,我已经看到许多指令(Why does Assert.AreEqual(T obj1, Tobj2) fail with identical objectsUnit Test Assert.AreEqual failedAssert.AreEqual fails with the same type)来为我的类实现Equals/IEqualityComparer

在尝试这样做时,我无法让它工作,并将问题缩小到以下问题。

我的班级主题需要测试。在 SubjectTestEqual 中,我希望两个实例相等,但结果是“Assert.AreEqual 失败。此外,如果我调试代码,我看不到对 Equals 的调用也不是 GetHashCode

基于神秘的“默认属性检查类型 T 是否实现 System.IEquatable 接口,如果是,则返回一个使用该实现的 EqualityComparer。否则,它返回一个使用 Object.Equals 和 Object 覆盖的 EqualityComparer .GetHashCode 由 T 提供。”在EqualityComparer.Default Property documentation中,我也试过用IEquatable(取消注释代码看看效果)无济于事。

public class Subject : IEqualityComparer<Subject>//, IEquatable<Subject>
{
    public int Id { get; set; }

    public Subject(int name) { Id = name; }

    public bool Equals(Subject x, Subject y)
    { return (x.Id == y.Id); }

    public int GetHashCode(Subject obj)
    { return Id; }

    //public bool Equals(Subject other)
    //{ return Equals(this, other); }
}

[TestClass]
public class SubjectTest
{
    [TestMethod]
    public void SubjectTestEqual()
    { Assert.AreEqual<Subject>(new Subject(1), new Subject(1)); }

    [TestMethod]
    public void SubjectTestSame()
    {
        Subject test = new Subject(1);
        Assert.AreEqual<Subject>(test, test);
    }
}

任何人都可以对此有所了解吗?

【问题讨论】:

  • 根据其他答案,我想出了某种答案。见下文。

标签: c# c#-4.0


【解决方案1】:

您必须重写基本方法才能调用您的方法:

public override bool Equals(object obj)
{ ... }

public override int GetHashCode()
{ ... }

但您必须应用与 EqualsGetHashcode 基本方法相同的签名。

【讨论】:

  • 我刚刚添加了:IEqualityComparer,从不关心签名。我应该注意到我没有被警告使用覆盖任何基本方法。
【解决方案2】:

感谢您的回答,他们在这方面进一步帮助了我,但他们都没有完全帮助我。

如果我像这样覆盖对象的 Equals 和 GetHashCode 方法:

public override bool Equals(object obj)
{ return true; }

public override int GetHashCode()
{ return 0; }

方法被调用,我的测试将成功。但是,当然,我不想为对象类型的参数实现这些方法。为什么我的班级会提供将实例与任何对象实例进行比较?

所以,我将此方法修改为静态:public **static** bool Equals(Subject x, Subject y) 并引入了一种新的测试方法(为简洁起见,所有测试都在一个方法中):

    [TestMethod]
    public void SubjectTestWithSubjectEquals()
    {
        Assert.IsTrue(Subject.Equals(new Subject(1), new Subject(1)));
        Assert.IsTrue(new Subject(1).Equals(new Subject(1)));
        Assert.IsFalse(Subject.Equals(new Subject(1), new Subject(2)));
        Assert.IsFalse(new Subject(1).Equals(new Subject(2)));
    }

这些测试将会成功。但是,我不确定这是否是一个好方法。一方面,要让Assert.IsTrue((new Subject(1)) == (new Subject(1))); 成功,我需要实现:

public static bool operator ==(Subject x, Subject y)
    { return Equals(x, y); }

这也需要:

public static bool operator !=(Subject x, Subject y)
    { return !Equals(x, y); }

另外,更进一步,希望能够比较主题类型的列表,我尝试了这个测试:

[TestMethod]
public void SubjectTestWithCollections()
{
    var list1 = new List<Subject>() { new Subject(1), new Subject(2) };
    var list2 = new List<Subject>() { new Subject(1), new Subject(2) };
    CollectionAssert.AreEqual(list1, list2);
}

这将失败,显然,每个项目都将使用 Assert.AreEqual 进行比较,而不是 Subject.Equals。我见过issues with CollectionAssert.AreEquivalent,这让我想知道这是否主要是由 Microsoft.VisualStudio.TestTools.UnitTesting 的实现方式引起的,还是我不应该对 Equals 大惊小怪。

【讨论】:

    【解决方案3】:

    您的 TestClass 违反了Object.Equals 的合同。 Assert.AreEqual 非常合理地依赖该合同。

    文档状态(在要求列表中):

    x.Equals(空引用(Visual Basic 中为无))返回 false。

    【讨论】:

    • 什么是 Object.Equals 的“契约”?签名?你为什么从文档中选择那句话?我没有传递任何空值。
    【解决方案4】:

    Assert.AreEqual 将专门使用传入的第一个参数的Equals 方法。它现在知道您的IEqualityComparer 实现,因此不会使用它。

    通常,当一个类需要比较对象是否相等时,它会有一个IEqualityComparer 的可选参数,该类可以使用它来进行比较,而不是对象本身的Equals 方法,但是Assert.AreEqual 接受没有这样的参数。这意味着要使用Assert.AreEqual,您需要覆盖从object 继承的Equals 方法,即:

    public override bool Equals(object obj){}
    

    一个类型实现它自己的类型的IEqualityComparer 几乎没有意义。 IEqualityComparer 的想法是它允许您比较其他类型是否相等,而不是“你自己”。

    【讨论】:

    • 我正在努力掌握这一点。我正在使用通用版本“public static void AreEqual(T expected, T actual)”。因此,传入的第一个参数是 Subject 类型。感谢您阐明 IEqualityComparer 的含义。如果我理解正确,在 Subject 上实现 IEqualityComparer 将允许我将 Subject 实例与 Subject2 实例进行比较。
    • @R.Schreurs 不,当您无法控制 Equals 的定义或可能有多个定义时,IEqualityComparer 允许您比较两个类。因此,也许您想创建一个 IEqualityComparer&lt;string&gt; 来进行不区分大小写的比较,或者编写一个 IEqualityComparer&lt;List&lt;Subject&gt;&gt; 来根据列表中的值而不是列表的引用来比较列表,等等。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-09-25
    • 1970-01-01
    • 2015-03-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多