为什么当我比较两个空列表时,测试中的“Assert.AreEqual”失败了?

10

我有一个名为MyCustomClass的类:

public MyCustomClass
{
    public MyCustomClass()
    {
        MyObject = new List<MyCustomObject>();
    }

    public List<MyCustomObject> MyObject {get; set;}
}

在测试中:

List<MyCustomObject> aux = new List<MyCustomObject>();
MyCustomClass oClass = new MyCustomClass();
Assert.AreEqual(aux, oClass.MyObject)
测试失败了,为什么?每个属性、静态成员等都是一样的。
3个回答

19

在这种情况下,Assert.AreEqual将检查这两个对象是否相同,但它们并不相同。你应该使用CollectionAssert.AreEqual代替,它会返回true,如果这两个"具有相同的元素、相同的顺序和数量"。


+1 你是在问题被问出来之前就开始回答了吗 :) ? 顺便说一下,AreEqual 的字符串重载实际上比较的是值而不是“对象相同”。 - Alexei Levenkov
@AlexeiLevenkov的AreEqual方法只是调用对象的Equals方法。string类的Equals方法比较值,而List类的实现则比较引用。每个类都可以自己决定如何实现,不像有特别支持字符串或其他类似的东西。 - Servy
@Servy,说得好。我会避免在C#/.Net中比较对象时使用“same”这个词 - 而是明确地说“同一引用”、“通过运算符=相同”、“通过Equals相同”...(顺便说一句,我喜欢你删除了你的回答) - Alexei Levenkov

4
作为已经回答的内容,两个元素为零的相同类型列表并不被视为相等。
原因在于,AreEqual实际上调用aux.AreEqual(oClass.MyObject),使用对象自身的相等性实现。由于这没有覆盖List<T>的相等性实现,所以它会退回到Object中的实现,即一个简单的引用相等性检查。这两个列表显然不是同一引用,因此它们不被视为相等。
由于Equals方法在Object上存在且为虚拟的,因此您自己的类可以重写Equals,以提供与引用相等性不同的另一种相等性概念。这是在像String这样的对象上完成的,即使对于不同的引用,如果数据相同,则它们也相等。

0

我使用 dotPeek 对位于 Microsoft.VisualStudio.QualityTools.UnitTestFramework GAC 程序集中的 Assert.AreEqual 进行了反编译,并发现 Assert.AreEqual(aux, oClass.MyObject) 最终将导致以下调用,其中 auxexpectedoClass.MyObjectactual

object.Equals((object) expected, (object) actual)

从静态object.Equals(Object, Object)文档中我们可以读到:

静态Equals(Object, Object)方法指示两个对象objA和objB是否相等。它还允许您测试值为null的对象的相等性。它按以下方式比较objA和objB的相等性:

它确定这两个对象是否表示相同的对象引用。如果是,则该方法返回true。此测试等效于调用ReferenceEquals方法。此外,如果objA和objB都为null,则该方法返回true。

它确定objA或objB是否为null。如果是,则返回false。

如果这两个对象不表示相同的对象引用且都不为null,则调用objA.Equals(objB)并返回结果。这意味着如果objA覆盖了Object.Equals(Object)方法,则会调用此覆盖。

现在,List<T> 被认为是引用类型,我们知道你要比较的这两个列表都不为空,因此你的两个对象之间的最终比较将是

expected.Equals(actual)

由于 List<T> 没有重写 Equals 方法,因此它使用基本对象实现进行引用比较,因此失败了(expectedactual 分别被 "newed")。

你想要的是 结构比较,即您列表中元素的成对相等。请参阅 @ReedCopsey 的答案以获取正确的断言(CollectionAssert.AreEqual)。


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