Interesting facts about C# Dictionary object

This info may be a bit of referesher for senior c# guys but for novice developers, this is going to be very useful. We all know about the famous C# dictionary object which takes in key-value pairs and internally uses buckets-linked list structure for storage and retrieval. Because of its fast lookup time, it has become a popular choice however, it is very useful if you know certain facts about how it compares stuff under the hood.

For example,

Lets say you have a dictionary object say, Dictionary<Person, bool> persons = new <Person, bool>. Here person is a user defined c# class with name & age properties. Now you create a new person called fred i.e. Person fred = new Person();”fred” ; fred.age=30;
But you haven’t yet added it to dictionary. However, if you do person[fred] = true, you will find that now the dictionary count is 1!

So, what has just happened? Clearly, we haven’t done anything like person.Add(fred). But, the new person is still added to the dictionary just because you tried to set it(as a key in dictionary) to true!   Lets see what dictionary did internally but first lets step a little step back…

Now, we all know that if you wish to compare two objects directly using “==”, then you must overload the operator “==”. This would also force you to overload its counterpart operator “!=” for obvious reasons. If you have a tool like resharper, if you press control and click on “==”, it will automatically navigate to overloaded operator implementation confirming that now, any comparision will hook into your code.

Also, let say you have defined a Clone method on Person which returns a new Person object with exactly the same name and age. You have also overriden the Equals methods. So now, when you do persons.Contains(fred.clone), it will call your overriden Equals method and return true/false whatever you choose to return based on your own logic.

But going back to our first situation,

with the above implementation of person object:

doing (fred == fred.clone()); will return true assuming that your overloaded operator just checks name and age and returns true if they are equal

doing person[fred] =  true; and a check using person.contains(fred) returns true, assuing that your overriden equals method just checks name and age and returns true if they are equal

however, on an empty dictionary object,

doing person[fred] = true; will increase the dictionary count by one

then, person[fred.clone] = true; will again the increase the dictionary count by one

and if you do, person[fred] = false; will not increase the count by one, implying that here, dictionary is treating fred & fred.clone as not equal.

the point to note here is that when you try something like this: person[fred.clone()] = true; the dictionary count goes up i.e. it treats it as a different object and adds it! it will not call your overriden Equals method or your ovreloaded method for operator “==” to check what should be fred.clone() be treated as. This is because when Dictionary object does a lookup for its internal keys, it goes by hash codes.  Internally it tried to locate the hashcode of the object being used as a key in its collection. Lets say fred’s  hascode is AAxxAA and fred.clone() hashcode is AAccAA. This are default system provided hascode. So hashcodes are different and hence, fred.clone() is not found in the dictionary’s keys collection, and therefore the dictionary object will very quitely add it in the collection and set it to the boolean value. Some people may argue that it should throw a KeyNotFound exception, but for some reason it doesn’t. Hence, person[fred.clone()] will result in a new object being added in dictionary.

If you wanted fred and fred.clone to be treated as equal by Dictonary<>, then you would have to override the System.Object’s GetHashCode() method and provide your own implementation for GetHashCode(). In this example, you can return the same HashCode for the person whose name and age are equal. Then when you do the above steps, Dictionary<> count will not go up.