.NET字典具有相同的键和值,但不是“相等”

时间:2010-02-08 00:52:24

标签: c# .net dictionary equality

此测试失败:

using Microsoft.VisualStudio.TestTools.UnitTesting;        

[TestMethod()]
        public void dictEqualTest() {
            IDictionary<string, int> dict = new Dictionary<string, int>();
            IDictionary<string, int> dictClone = new Dictionary<string, int>();

        for (int x = 0; x < 3; x++) {
            dict[x.ToString()] = x;
            dictClone[x.ToString()] = x;
        }

        Assert.AreEqual(dict, dictClone); // fails here
        Assert.IsTrue(dict.Equals(dictClone)); // and here, if the first is commented out
        Assert.AreSame(dict, dictClone); // also fails
    }

我是否误解了Dictionary如何运作?

我正在寻找等价于.equals()的Java,而不是试图检查引用相等。

6 个答案:

答案 0 :(得分:21)

字典类不会覆盖从MSDN doco中看到的Object.Equals方法:

http://msdn.microsoft.com/en-us/library/bsc2ak47.aspx

  

确定是否指定   Object等于当前的Object。

看到你正在进行单元测试,你的 Assert类应该提供一个测试方法来测试两个集合是否相同。

Microsoft单元测试框架提供CollectionAssert类用于比较集合:

http://msdn.microsoft.com/en-us/library/microsoft.visualstudio.testtools.unittesting.collectionassert_members%28VS.80%29.aspx

编辑字典实现ICollection界面,你能看出它是否有效吗?您可能需要使用this overload来比较两个字典条目。

编辑 Hmm IDictionary没有实现ICollection,这有点痛苦。然而,这有效(虽然是黑客):

IDictionary<string, int> dict = new Dictionary<string, int>();
IDictionary<string, int> dictClone = new Dictionary<string, int>();

for(int x = 0; x < 3; x++) {
    dict[x.ToString()] = x;
    dictClone[x.ToString()] = x;
}

CollectionAssert.AreEqual((System.Collections.ICollection)dict, (System.Collections.ICollection)dictClone);

上述方法适用于Dictionary的实例,但是如果您正在测试返回IDictionary的方法,则如果该实施更改,则可能会失败。我的建议是将代码更改为使用Dictionary而不是IDictionary(因为IDictionary不是只读的,因此您不会通过使用它代替concreate {{1}来隐藏所有这些})。

答案 1 :(得分:9)

如果您对如何从单元测试角度解决此问题特别感兴趣:

试试这个

CollectionAssert.AreEquivalent(dict.ToList(), dictClone.ToList());

<强>解释

extension methods on IDictionary - 例如.ToList() - 可在.Net 3.5及更高版本中使用,它会将字典转换为KeyValuePair的集合,可以轻松地与CollectionAssert.AreEquivalent进行比较。

他们甚至会提供合理有用的错误消息!用法示例:

IDictionary<string, string> d1 = new Dictionary<string, string> {
    { "a", "1"}, {"b", "2"}, {"c", "3"}};

IDictionary<string, string> d2 = new Dictionary<string, string> {
    {"b", "2"}, { "a", "1"}, {"c", "3"}}; // same key-values, different order

IDictionary<string, string> d3 = new Dictionary<string, string> {
    { "a", "1"}, {"d", "2"}, {"c", "3"}}; // key of the second element differs from d1

IDictionary<string, string> d4 = new Dictionary<string, string> {
    { "a", "1"}, {"b", "4"}, {"c", "3"}}; // value of the second element differs from d1

CollectionAssert.AreEquivalent(d1.ToList(), d2.ToList());
//CollectionAssert.AreEquivalent(d1.ToList(), d3.ToList()); // fails!
//CollectionAssert.AreEquivalent(d1.ToList(), d4.ToList()); // fails!

// if uncommented, the 2 tests above fail with error:
//   CollectionAssert.AreEquivalent failed. The expected collection contains 1
//   occurrence(s) of <[b, 2]>. The actual collection contains 0 occurrence(s).     

答案 2 :(得分:7)

问题在于这行代码:

Assert.AreEqual(dict, dictClone)

您正在比较不相等的对象引用。

答案 3 :(得分:4)

我使用了一种扩展方法来检查两个序列是否相等

public static bool CheckForEquality<T>(this IEnumerable<T> source, IEnumerable<T> destination)
{
    if (source.Count() != destination.Count())
    {
        return false;
    }

    var dictionary = new Dictionary<T, int>();

    foreach (var value in source)
    {
        if (!dictionary.ContainsKey(value))
        {
            dictionary[value] = 1;
        }
        else
        {
            dictionary[value]++;
        }
    }

    foreach (var member in destination)
    {
        if (!dictionary.ContainsKey(member))
        {
            return false;
        }

        dictionary[member]--;
    }

    foreach (var kvp in dictionary)
    {
        if (kvp.Value != 0)
        {
            return false;
        }
    }

    return true;
}

答案 4 :(得分:1)

您完全不了解参考类型的工作原理。

Dictionary不会覆盖object.Equals()。因此,它使用引用相等 - 基本上,如果两个引用都指向同一个实例,它们是相等的,否则它们不是。

答案 5 :(得分:0)

NUnit类CollectionAssert有一个AreEquivalent method which accepts IEnumerable as parameters,所以在这种情况下它就像

CollectionAssert.AreEquivalent(dict, dictClone);

因为Dictionary实现了IEnumerable