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

时间:2022-07-06 13:02:34

This test fails:

此测试失败:

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
    }

Am I misunderstanding something about how a Dictionary works?

我是否误解了词典如何运作?

I'm looking for the Java equivalent of .equals(), not trying to check referential equality.

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

5 个解决方案

#1


20  

Dictionary class does not override Object.Equals method as seen from MSDN doco:

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

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

Determines whether the specified Object is equal to the current Object.

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

Seeing that you are doing unit testing, your Assert class should provide a test method for testing if two collections are the same.

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

Microsoft Unit testing framework provides CollectionAssert class for the purpose of comparing collections:

Microsoft Unit测试框架提供了CollectionAssert类,用于比较集合:

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

EDIT Dictionary implements ICollection interface, can you see if that just works? You might need to use this overload to compare two dictionary entries.

EDIT Dictionary实现了ICollection接口,你能看出它是否有效吗?您可能需要使用此重载来比较两个字典条目。

EDIT Hmm IDictionary does not implement ICollection, which is a bit of a pain. This however works (albeit a hack):

编辑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);

THe above approach will work for instances of Dictionary, however if you are testing a method that returns IDictionary it might fail if the implmentation changes. My advice is to change the code to use Dictionary instead of IDictionary (since IDictionary is not readonly, so you are not hiding all that much by using that instead of concreate Dictionary).

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

#2


9  

If you are specifically interested in how you can fix this from unit testing perspective:

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

Try this

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

Explanation

There are extension methods on IDictionary - such as .ToList() - available in .Net 3.5 and up, which will convert the dictionary into a collection of KeyValuePair that can be easily compared with CollectionAssert.AreEquivalent.

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

They'll even give reasonably helpful error messages! Example usage:

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

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).     

#3


7  

The problem is with this line of code:

问题在于这行代码:

Assert.AreEqual(dict, dictClone)

You are comparing object references, which aren't equal.

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

#4


4  

I have used an extension method that checks two sequences for equal items

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

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;
}

#5


1  

You are completely not understanding how reference types work.

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

Dictionary does not override object.Equals(). Thus, it uses reference equality - basically, if both references are pointing to the same instance, they're equal, otherwise they aren't.

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

#1


20  

Dictionary class does not override Object.Equals method as seen from MSDN doco:

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

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

Determines whether the specified Object is equal to the current Object.

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

Seeing that you are doing unit testing, your Assert class should provide a test method for testing if two collections are the same.

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

Microsoft Unit testing framework provides CollectionAssert class for the purpose of comparing collections:

Microsoft Unit测试框架提供了CollectionAssert类,用于比较集合:

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

EDIT Dictionary implements ICollection interface, can you see if that just works? You might need to use this overload to compare two dictionary entries.

EDIT Dictionary实现了ICollection接口,你能看出它是否有效吗?您可能需要使用此重载来比较两个字典条目。

EDIT Hmm IDictionary does not implement ICollection, which is a bit of a pain. This however works (albeit a hack):

编辑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);

THe above approach will work for instances of Dictionary, however if you are testing a method that returns IDictionary it might fail if the implmentation changes. My advice is to change the code to use Dictionary instead of IDictionary (since IDictionary is not readonly, so you are not hiding all that much by using that instead of concreate Dictionary).

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

#2


9  

If you are specifically interested in how you can fix this from unit testing perspective:

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

Try this

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

Explanation

There are extension methods on IDictionary - such as .ToList() - available in .Net 3.5 and up, which will convert the dictionary into a collection of KeyValuePair that can be easily compared with CollectionAssert.AreEquivalent.

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

They'll even give reasonably helpful error messages! Example usage:

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

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).     

#3


7  

The problem is with this line of code:

问题在于这行代码:

Assert.AreEqual(dict, dictClone)

You are comparing object references, which aren't equal.

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

#4


4  

I have used an extension method that checks two sequences for equal items

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

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;
}

#5


1  

You are completely not understanding how reference types work.

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

Dictionary does not override object.Equals(). Thus, it uses reference equality - basically, if both references are pointing to the same instance, they're equal, otherwise they aren't.

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