.NET Dictionaries have same keys and values, but aren't “equal”

前端 未结 6 1216
旧时难觅i
旧时难觅i 2021-01-17 08:48

This test fails:

using Microsoft.VisualStudio.TestTools.UnitTesting;        

[TestMethod()]
        public void dictEqualTest() {
            IDictionary<         


        
相关标签:
6条回答
  • 2021-01-17 09:04

    The problem is with this line of code:

    Assert.AreEqual(dict, dictClone)
    

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

    0 讨论(0)
  • 2021-01-17 09:04

    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;
    }
    
    0 讨论(0)
  • 2021-01-17 09:12

    The NUnit class CollectionAssert has an AreEquivalent method which accepts IEnumerable as parameters, so in that case it's as simple as

    CollectionAssert.AreEquivalent(dict, dictClone);
    

    because Dictionary implements IEnumerable.

    0 讨论(0)
  • 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.

    0 讨论(0)
  • 2021-01-17 09:19

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

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

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

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

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

    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 Hmm IDictionary does not implement ICollection, which is a bit of a pain. This however works (albeit a hack):

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

    0 讨论(0)
  • 2021-01-17 09:19

    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.

    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).     
    
    0 讨论(0)
提交回复
热议问题