Equals () for .NET Dictionary / IDictionary vs equals () for Java Map

Nostalgic for Collections.unmodifiableMap() , I used a IDictionary only IDictionary wrapper based on this discussion , and my unit test quickly ran into a problem:

 Assert.AreEqual (backingDictionary, readOnlyDictionary); 

fails even if the key-value pairs match. I played a little, and it seems, at least (thanks to Simony)

 Assert.AreEquals (backingDictionary, new Dictionary<..> { /* same contents */ }); 

really passes.

I quickly looked through Dictionary and IDictionary , and to my surprise, I could not find the equivalent of the Java Map convention that two Maps with equal entrySet()s should be equal. (The docs say the Dictionary - not an IDictionary - overrides Equals() , but doesn't say that this override does.)

Thus, it seems that the key value in C # is a property of a particular Dictionary class, not an IDictionary . It is right? As a rule, this is true for the entire framework of System.Collections ?

If so, I would be interested to read some discussion of why MS chose this approach, as well as which preferred way would be to check if the contents of the collection are equal in C #.

And finally, I would not mind a pointer to a well-tested implementation of ReadOnlyDictionary . :)


ETA: To be clear, I'm not looking for suggestions for testing my implementation - this is relatively trivial. I am looking for guidance on which contract these tests should perform. And why.


ETA: People, I know that IDictionary is an interface, and I know that interfaces cannot implement methods. This is the same in Java. However, the Java Map interface documents the expectation of specific behavior from the Equals() method. Of course, there must be .NET interfaces that do such things, even if collection interfaces are not among them.

+7
java equality immutability collections
source share
6 answers

For later readers, here's what I was told / were able to find out:

  • The contract for .NET collections, unlike Java collections, does not include any specific behavior for Equals() or GetHashCode() .
  • The LINQ Enumerable.SequenceEqual() extension method will work for ordered collections, including dictionaries that are IEnumerable<KeyValuePair> ; KeyValuePair is a structure, and its Equals method uses reflection to compare content.
  • Enumerable provides another extension of the methods that can be used for drilling as well as checking equality of content, such as Union() and Intersect() .

I come up with the idea that, conveniently, like Java methods, they might not be the best idea if we talk about mutable collections and the typical implicit semantics of Equals() - that two equal objects are interchangeable equal NET does not provide very good support for immutable collections, but the open source PowerCollections library.

+2
source share

Overriding values ​​are usually performed only with classes that have a degree of semantics of the value (for example, string ). Reference equality is what people most often refer to most reference types and a good default, especially in cases that may be less clear (these are two dictionaries with exactly the same key-value pairs, but with different equalities-comparators [and therefore, adding the same additional key-value pair could make them different now] equal or not?), or where the equivalence of equality will not be often seen.

In the end, you're looking for a case where two different types are considered equal. The designation of equality probably still will not give you the opportunity.

Moreover, you can always create your own comparisons compiler quickly enough:

 public class SimpleDictEqualityComparer<TKey, TValue> : IEqualityComparer<IDictionary<TKey, TValue>> { // We can do a better job if we use a more precise type than IDictionary and use // the comparer of the dictionary too. public bool Equals(IDictionary<TKey, TValue> x, IDictionary<TKey, TValue> y) { if(ReferenceEquals(x, y)) return true; if(ReferenceEquals(x, null) || ReferenceEquals(y, null)) return false; if(x.Count != y.Count) return false; TValue testVal = default(TValue); foreach(TKey key in x.Keys) if(!y.TryGetValue(key, out testVal) || !Equals(testVal, x[key])) return false; return true; } public int GetHashCode(IDictionary<TKey, TValue> dict) { unchecked { int hash = 0x15051505; foreach(TKey key in dict.Keys) { var value = dict[key]; var valueHash = value == null ? 0 : value.GetHashCode(); hash ^= ((key.GetHashCode() << 16 | key.GetHashCode() >> 16) ^ valueHash); } return hash; } } } 

This would not have served all the possible cases when you need to compare dictionaries, but then it was my point.

Filling out the BCL “maybe what they mean”, equality methods would be a nuisance, not help.

+3
source share

I would suggest using CollectionAssert.AreEquivalent () from NUnit. Assert.AreEqual () is really not intended for collections. http://www.nunit.org/index.php?p=collectionAssert&r=2.4

+2
source share
 public sealed class DictionaryComparer<TKey, TValue> : EqualityComparer<IDictionary<TKey, TValue>> { public override bool Equals( IDictionary<TKey, TValue> x, IDictionary<TKey, TValue> y) { if (object.ReferenceEquals(x, y)) return true; if ((x == null) || (y == null)) return false; if (x.Count != y.Count) return false; foreach (KeyValuePair<TKey, TValue> kvp in x) { TValue yValue; if (!y.TryGetValue(kvp.Key, out yValue)) return false; if (!kvp.Value.Equals(yValue)) return false; } return true; } public override int GetHashCode(IDictionary<TKey, TValue> obj) { unchecked { int hash = 1299763; foreach (KeyValuePair<TKey, TValue> kvp in obj) { int keyHash = kvp.Key.GetHashCode(); if (keyHash == 0) keyHash = 937; int valueHash = kvp.Value.GetHashCode(); if (valueHash == 0) valueHash = 318907; hash += (keyHash * valueHash); } return hash; } } } 
+1
source share

Thus, it looks like key value equality in C # is a Dictionary property of a particular class, not an IDictionary interface. It is right? Does this generally apply to the entire System.Collections structure?

If so, I would be interested to read some discussion of why MS chose the Approach.

I think this is quite simple - IDictionary is an interface, and interfaces cannot have any implementations, and in .NET the world of equality of two objects is determined using the Equals method. Thus, it is not possible to override Equals for an IDictionary to have "key value equality".

+1
source share

You made a big mistake in your original post. You talked about the Equals() method in the IDictionary interface. This is the point!

Equals () is a virtual System.Object method that classes can override. Interfaces do not implement methods at all . Instead, interface instances are reference types, inheriting from System.Object and potentially declaring an override of Equals() .

Now the point ... System.Collections.Generic.Dictionary<K,V> does not override the equal. You said that you implemented your IDictionary in your own way and reasonably overcame Equals, but look at your own code

 Assert.AreEqual (backingDictionary, readOnlyDictionary); 

This method is basically implemented as return backingDictionary.Equals(readOnlyDictionary) and here again is the point.

The Basic Equals () method returns false, if two objects are instances of different classes, you cannot control this. Otherwise, if two objects are of the same type, each element is compared through reflection (just members, not properties) using the Equals() approach instead of == (this is what the manual calls a “comparison of values” instead of “reference compare ")

So firstly, I won’t be surprised if Assert.AreEqual (readOnlyDictionary,backingDictionary); succeed because it will call the custom Equals method.

I have no doubt that the approaches of other users in this topic work, but I just wanted to explain to you what was a mistake in your initial approach. Of course, Microsoft would better use the Equals method, which compares the current instance with any other IDictionary instance, but again, this goes beyond the scope of the Dictionary class, which is a public stand-alone class and is not intended to be the only public implementation of IDictionary. For example, when you define an interface, factory, and a protected class that implements it in a library, you can compare the class with other instances of the base interface, and not with the class itself, which is not public.

I hope to help you. Greetings.

0
source share

All Articles