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

Viewed 18543

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.

6 Answers

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

The problem is with this line of code:

Assert.AreEqual(dict, dictClone)

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

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

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.

Related