2

我已经构建了一些复杂的对象,并且正在尝试通过进行一些单元测试来验证它是否正常工作。这涉及比较一些 List(Of T),所以我尝试使用 CollectionAssert。现在我遇到了一些奇怪的事情。

首先,我使用 CollectionAssert.AreEqual 来查看第一个列表是否相等。这个断言通过了。但是为了简单起见,我想使用 CollectionAssert.AreEqual,这样我就不必以正确的顺序创建预期的对象,所以我开始尝试。使用完全相同的代码,CollectionAssert.AreEquivalent 失败。我会说这很奇怪,因为等价是比等价更宽松的断言,对吧?我收到此错误:

CollectionAssert.AreEquivalent failed. The expected collection contains 1 occurrence(s) of <MyObject>. The actual collection contains 0 occurrence(s).

我试过调试,但我没有让它调试.Net框架,即使我设置了下载符号文件。所以我只能看到它进入我的自定义 Equals 函数一次 - 返回 true - 然后断言失败。两个对象都有两个元素。调用堆栈是(以相反的顺序):

  • CollectionAssert.AreEquivalent
  • CollectionAssert.AreEquivalent(重载)
  • CollectionAssert.FindMisMatchedElement
  • Generic.Dictionary(Of Object, int).TryGetValue
  • Generic.Dictionary(Of Object, int).FindEntry
  • Generic.ObjectEqualityComparer.Equals
  • 我的自定义等于

现在我正在写这篇文章,一个想法出现了,我看到了一个潜在的问题。我看到它在内部使用字典。哪个可能充当某种哈希图,其中 int 是实际列表中的索引?这是否意味着我需要实现自定义 IEqualityComparer,而不是覆盖 equals?那么我的 getHashCode() 应该如何?(我猜这很关键,因为我认为它可能被用作字典中的键?)

4

1 回答 1

2

您走在正确的轨道上:问题确实是您在 overrideGetHashCode时没有覆盖Equals

这是重现您的问题的示例:

void Main()
{
    var a = new []{new Broken{Foo="a"}, new Broken{Foo="b"}};
    var b = new []{new Broken{Foo="a"}, new Broken{Foo="b"}};

    CollectionAssert.AreEqual(a, b);
    CollectionAssert.AreEquivalent(a, b);
}

class Broken
{
    public string Foo {get;set;}

    public override bool Equals(object obj)
    {
        return Foo == ((Broken)obj).Foo;
    }
}

正如您正确指出的那样,CollectionAssert.AreEquivalent使用 a Dictionary,它用于计算每个唯一元素在集合中出现的频率。

问题不在于哈希码可能有冲突,而是如果两个应该被认为相等的元素实际上永远不会使用Equals它们返回的哈希码进行GetHashCode比较。


你可能也对这个问题感兴趣:

为什么在重写 Equals 方法时重写 GetHashCode 很重要?


此外,我的 equals 函数中有一些逻辑,它不是直接比较所有字符串 1 对 1,所以这意味着我基本上会实现这个功能两次,以确保相等的对象获得相同的哈希码,对吗?

不必要。an 的性能Dictionary取决于散列算法(对象的散列值在用作键时也不应该改变)。

如果您可以忍受一些性能损失(可能可以忽略不计),您可以使用比您在Equals方法中使用的更简单的方法来计算哈希值(并接受更多的哈希冲突)。如果两个对象的哈希值相等,Equals则无论如何都会调用。(事实上​​,您可以每次只返回相同的值,例如 1)。


文档中的相关部分:

Object.GetHashCode

用作 Hashtable 对象中的键的对象也必须覆盖 GetHashCode 方法,因为这些对象必须生成自己的哈希码

如果两个对象比较相等,则每个对象的 GetHashCode 方法必须返回相同的值。但是,如果两个对象不比较相等,则两个对象的 GetHashCode 方法不必返回不同的值。

于 2013-04-06T10:33:13.153 回答