为什么Assert.AreEqual()在比较之前要转换为对象?

11

我正在编写一些单元测试,以下断言失败:

Assert.AreEqual(expected.Episode, actual.Episode);

如果我使用以下方式进行调用,则会成功:
Assert.IsTrue(expected.Episode.Equals(actual.Episode));

我曾经认为 Assert.AreEqual() 最终会调用被给定类型的 Equals() 方法,就像这里的 Episode.Equals()
然而,在 Microsoft.VisualStudio.TestTools.UnitTesting.Assert 的底层,我发现了以下代码(由 ReSharper 反编译):
public static void AreEqual<T>(T expected, T actual, string message, params object[] parameters)
{
    if (object.Equals((object)expected, (object)actual))
        return;
    Assert.HandleFail...
}

这对我来说意味着AreEqual()方法将expectedactual都强制转换为object,以使用基类Equals()方法而不是我在Episode类中重载的方法。基类方法将简单地检查引用是否相同,但它们并不相同。
我的两个问题是:
  1. 我的解释是否正确,还是我漏掉了什么?
  2. 为什么框架要强制使用object.Equals()而不是该方法的重载?
如果相关,请看一下我的方法:
public bool Equals(Episode other)
{
    return Number == other.Number &&
           CaseNote.Equals(other.CaseNote) &&
           Patient.Equals(other.Patient);
}

离题:ReSharper的哪个版本可以反编译? - sll
我不确定它存在了多久,但至少在我的经验中是v5...第一次在内部对象上按F12(转到声明)时,它会询问您想要做什么;其中一个选项是反编译。Jetbrains还提供了一个免费的独立反编译器。 - Sir Crispalot
1
顺便提一下,所展示的方法是一个重载,而不是覆盖 - Marc Gravell
2个回答

6
它使用 object.Equals(object,object) 处理以下内容:
  • 它们是否是同一引用?
  • 它们是否是空引用(null)?
并在处理这些情况之后使用x.Equals(y)。 它必须将它们强制转换为 object 因为这是 object.Equals (object,object)的参数类型。强制转换为 object 还可以避免与Nullable<T>的复杂性(因为T? 将装箱为null 或常规装箱的T)。

但是,它也可以实现为:

 if (EqualityComparer<T>.Default.Equals(expected,actual))
    return;

它处理Nullable、IEquatable、struct vs class等其他场景,不需要任何装箱。但是:当前实现可以完成工作,偶尔的装箱也不是世界末日(而且:如果您的类型是类,则装箱甚至不是问题)。

请注意,通过将 Nullable<T> 强制转换为对象来检查空值会产生性能影响,可以避免这种情况(这不是你的错,要知道)。 - casperOne
@casperOne 确实,这就是为什么 如果我在写代码 的话,我会使用 EqualityComparer<T>,它不会出现这种问题。 - Marc Gravell
虽然这个答案是正确的,但它没有解释为什么 OP 看到了 false,而他期望看到 true。主要问题在于他只实现了 Equals(Episode other) 而没有同时实现 Equals(object other) - Servy

4

在您的代码中,您需要同时覆盖 Equals(object other) 方法(并且还需要覆盖 GetHashCode 方法)。

只需将以下内容添加到您的代码中即可:

public bool Equals(Episode other)
{
    return Number == other.Number &&
           CaseNote.Equals(other.CaseNote) &&
           Patient.Equals(other.Patient);
}

public override bool Equals(object other)
{
    Episode castOther = other as Episode;
    if(castOther == null)
        return false;
    return this.Equals(castOther);
}

public override int GetHashCode()
{
    //TODO: Implement using the members you used in "Equals(Episode other)"
    throw new NotImplmentedExecption();
}

记住,在 GetHashCode 中,如果两个对象相等,则它们必须返回相等的哈希码。这是一个快速的图表以帮助可视化。

enter image description here

您可能希望检查 CaseNotePatient 是否存在类似问题。


感谢您的有益回答。我意识到这实际上是导致我的代码对两个断言返回不同结果的原因。 - Sir Crispalot

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接