What can you use as keys in a C# dictionary?

12.2k views Asked by At

I come from a python world where only hashable objects may be used as keys to a dictionary. Is there a similar restriction in C#? Can you use custom types as dictionary keys?

4

There are 4 answers

4
Hans Passant On BEST ANSWER

The requirement for a dictionary key is that it is comparable and hashable. That's turtles all the way down in .NET, every type (other than pointer types) derives from System.Object and it is always comparable thanks to its Equals() method. And hashable thanks to its GetHashCode() method. So any .NET type automatically can be used as a key.

If you want to use your own type as the key then you only need to do something special if you want to re-define object identity. In other words, if you need the ability for two distinct objects to be equal. You'd then override the Equals() method, typically comparing fields of the object. And then you must also override GetHashCode(), equal objects must generate the same hash code.

If the type cannot be changed or you want to customize the behavior especially for the Dictionary then you can pass a custom IEqualityComparer<> to the constructor. Keep in mind that the quality of the hash code you generate with your own GetHashCode() determines the dictionary efficiency.

7
Suhan On

Yes you can, just implement interface IEqualityComparer, override GetHashCode and Equals.

5
Adam Houldsworth On

Yes, the important thing with keys is that they implement (or have a good default implementation of) GetHashCode and Equals. The Dictionary<T, K> implementation can take advantage of generic IEqualityComparer<T>.

All custom types will come with a default implementation of GetHashCode and Equals because these are members of object, however, that default might not always be relevant to your type.

The dictionary first attempts to get the hash code to determine the bucket the value will land in. If there is a hash collision, it falls back onto equality (I think).

Do note that the type of key you use (class, struct, primitive type, etc) can yield different performance characteristics. In our code base we found that the default implementation of GetHashCode in struct wasn't as fast as overriding it ourselves. We also found that nested dictionaries perform better in terms of access time than a single dictionary with a compound key.

0
ChrisW On

I'd like to add that, these days, you can use C# record types as keys.

The benefit of record (instead of class) is that it now implements GetHashCode() etc. for you automatically (via compiler-generated magic).

  • For details see Records (C# reference) -- Value equality
  • Beware this Help doesn't say explicitly that you can use them as keys (but I believe you can)
  • Beware that I expect that Bad Things Would Happen if you mutate (change a property value of) an object that's being used as a key, so don't do that