views:

465

answers:

4

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  A: 
Assert.AreEqual(dict, dictClone)

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

dcp
+1, additionally I'm not sure if MS's test suite has collection compare tools, but I'm pretty sure NUnit does.
sixlettervariables
+2  A: 

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.

Anon.
ok, sweet! what equality check do I want to be using, then?
Rosarch
+3  A: 

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

Igor Zevaka
Looks good. But how do I convert from a `Dictionary` to `ICollection`? A `ICollection` only has one type argument.
Rosarch
Good question. See edit.
Igor Zevaka
I've been looking at that documentation but I can't find anything that I could use to compare dictionaries.
Rosarch
That is intriguing, but what `ICompare` would I use? Do I have to write my own?The cast to `ICollection` doesn't work. What type would I use as the argument? The key or the value?
Rosarch
If I cast to `ICollection` it works for me.
Igor Zevaka
Your above hack wouldn't compile for me. The error:Using the generic type 'System.Collections.Generic.ICollection<T>' requires '1' type arguments
Rosarch
Try `System.Collections.ICollection`.
Igor Zevaka
mmm that does work. hack hack hack hack.
Rosarch
+2  A: 

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;
}
Rohan West
+1 Thank you this lesson in Linq ! Being able to study this type of code is very valuable to a "mere mortal" like me :)
BillW
@BillW, There is nothing about LINQ here. This is a feature called extension method which of course was developed mainly for LINQ.
Fakrudeen