Generic Dictionary Equality Comparer I created the below comparer to allow me to use a generic dictionary as a key to another generic dictionary.

My GetHashCode implementation creates a hash based on all keys and their values; but I suspect its distribution could be improved?

Also my Equals method returns true only if the two arguments have exactly the same keys, with each key having exactly the same values.  However, potentially there's a more efficient approach to this comparison which I've neglected?

using System.Collections.Generic;
using System.Linq;

namespace MyCompany.Collections.Generic
{
    public class DictionaryEqualityComparer&lt;TKey, TValue&gt; : IEqualityComparer&lt;IDictionary&lt;TKey, TValue&gt;&gt;
    {
        readonly IEqualityComparer&lt;TKey&gt; keyComparer;
        readonly IEqualityComparer&lt;TValue&gt; valueComparer;

        public DictionaryEqualityComparer(): this(null, null){}
        public DictionaryEqualityComparer(IEqualityComparer&lt;TKey&gt; keyComparer, IEqualityComparer&lt;TValue&gt; valueComparer)
        {
            this.keyComparer = keyComparer ?? EqualityComparer&lt;TKey&gt;.Default;
            this.valueComparer = valueComparer ?? EqualityComparer&lt;TValue&gt;.Default;
        }

        public bool Equals(IDictionary&lt;TKey, TValue&gt; a, IDictionary&lt;TKey, TValue&gt; b)
        {
            if (a == null || b == null) return (a == null &amp;&amp; b == null);                //if either value is null return false, or true if both are null
            return a.Count == b.Count                                                   //unless they have the same number of items, the dictionaries do not match
                &amp;&amp; a.Keys.Intersect(b.Keys, keyComparer).Count() == a.Count             //unless they have the same keys, the dictionaries do not match
                &amp;&amp; a.Keys.Where(key =&gt; ValueEquals(a[key], b[key])).Count() == a.Count; //unless each keys' value is the same in both, the dictionaries do not match
        }

        public int GetHashCode(IDictionary&lt;TKey, TValue&gt; obj)
        {
            //I suspect there's a more efficient formula for even distribution, but this does the job for now
            long hashCode = obj.Count;
            foreach (var key in obj.Keys)
            {
                hashCode += (key?.GetHashCode() ?? 1000) + (obj[key]?.GetHashCode() ?? 0);
                hashCode %= int.MaxValue; //ensure we don't go outside the bounds of MinValue-MaxValue
            }
            return (int)hashCode; //safe conversion thanks to the above %
        }

        private bool ValueEquals(TValue x, TValue y)
        {
            return valueComparer.Equals(x, y);
        }

    }
}
You said you built this specifically so you can use dictionaries as keys in other dictionaries?

In that case, you've got a problem. Consider the following demonstration:

var keyA = new Dictionary&lt;string, int&gt; { ["a"] = 4 };
var keyB = new Dictionary&lt;string, int&gt; { ["a"] = 4 };

var comparer = new DictionaryEqualityComparer&lt;string, int&gt;();
var dict = new Dictionary&lt;Dictionary&lt;string, int&gt;, int&gt;(comparer);
dict[keyA] = 10;

keyA["b"] = 5;
var x = dict[keyA];    // KeyNotFoundException: different hash
var y = dict[keyB];    // KeyNotFoundException: same hash, but not equal to original key


After modifying keyA, its hash-code is different, so a key lookup will fail. So far so good - this seems to be what you intended.

However, a lookup with keyB will also fail. Its hash-code is the same as keyA's original hash-code, so that part of the lookup succeeds, but because of hash collisions dict needs to compare it with the original key... which fails because keyA no longer has the same content.

That's why, generally speaking, the hash-code of an object should never change during its lifetime.



Also, the idea behind a hash-code is to have a quick, cheap way to filter out objects that are obviously not equal. Inspecting every single key and value seems fairly heavy-handed.

As for what you want to achieve... I suspect that will require a custom dictionary implementation, because you'll need to retain the original content of each key for future comparisons. At that point, I'd start thinking about why I even wanted to do this in the first place - there's likely a better solution for the original problem.