views:

1123

answers:

3

I ran into this today when unit testing a generic dictionary.

System.Collections.Generic.Dictionary<int, string> actual, expected;
actual = new System.Collections.Generic.Dictionary<int, string> { { 1, "foo" }, { 2, "bar" } };
expected = new System.Collections.Generic.Dictionary<int, string> { { 1, "foo" }, { 2, "bar" } };
Assert.AreEqual(expected, actual); //returns false

fails except when actual == expected (object references are the same). Obviously, actual.Equals(expected) returns false as well.

Fine, but if the implementation of System.Collections.Generic.Dictionary<int, string>.Equals only does reference equality, what's the point of IEquatable? In other words, why is there no baked-in way to do value equality for generic collections?

Edit Thanks for the responses so far. Obviously my example is using value types, but I think my complaint holds for all objects. Why can't a generic collection equality just be a union of equalities of its types? Unexpected behavior doesn't really cut it since there are separate provisions for finding reference equality. I suppose this would introduce the constraint of collections only holding object that implement IEquatable, as Konrad Rudolph points out. However, in an object like Dictionary, this doesn't seem too much to ask.

+2  A: 

Dictionary<T,T>.Equals is inherited from Object.Equals and thus does a simple comparison of the object references.

Why do the generic collections not do value equality semantic? Because that may not be what you want. Sometimes you want to check if they're the same instance. What would the alternative do? Call Equals on each of the keys and values? What if these inherit Equals from Object? It wouldn't be a full deep comparison.

So it's up to you to provide some other semantic if and when necessary.

Mike Scott
Also, what if we have a Dictionary of objects contain itself? We'd probably get killed by mobs of programmers first, but we could end up in an infinite loop.
configurator
The alternative would be to provide a sane, consistent semantic of equality operations, not the amalgam it is currently. Unless I dig down into the inheritance stack, there's no way to tell what an Equals() is actually equating!
Noel
+1  A: 

Dictionary<TKey, TValue> does not implement IEquatable, as such there is no formal way to determine/know what comparing one such dictionary to another with the same contents would actually produce.

In fact, Dictionary<TKey, TValue> does not implement any of the comparison interfaces at all.

In my opinion, to have two objects compare themselves is a rather special thing, so it would've made much more sense to put that into an interface than just to put a default typically-unwanted implementation in the base Object class. It should've been a more advertised feature of a class than something every object can do, albeit not quite the way you expect it to.

But there you have it. It's there, and you need to know when it's going to be used.

Like in this case.

Lasse V. Karlsen
You could make note that test frameworks like NUnit provide the means to use simple things like myDictionary.Is.EqualTo(otherDictionary) which will use the ICollection interface.
sixlettervariables
That is correct, but that is code built into the .EqualTo method of the unit testing framework, not something built into the collection class itself. If you add outside code into the mix you can of course do anything you'd like.
Lasse V. Karlsen
Agree re: typically-unwanted implementation. And fair point to that Dictionary doesn't implement any comparison interfaces at all (I blame MSDN for my confusion on that point "if type TKey implements the System.IEquatable(T) generic interface, the default equality comparer uses that implementation."
Noel
Also note that the only way one Dictionary<TKey, TValue> instance would be able to 100% compare itself to another instance would be if both TKey and TValue implemented one of the comparison interfaces, and thus it would have to add limitations for only accepting generic types that did just that.
Lasse V. Karlsen
+2  A: 

In other words, why is there no baked-in way to do value equality for generic collections?

Probably because it's hard to formulate in generic terms, since this would only be possible if the value type (and key type) of the dictionary also implemented IEquatable. However, requiring this would be too strong, making Dictionary unusable with a lot of types that don't implement this interface.

This is an inherent problem with constrained generics. Haskell provides a solution for this problem but this requires a much more powerful and complicated generics mechanism.

Notice that something similar is true for IComparable in comparison with containers, yet there is support for this, using Comparer<T>.Default if necessary.

Konrad Rudolph