CodeSOD: The Key to Using Dictionaries
It's easy to use dictionaries/maps to solve the wrong kinds of problems, but deep down, what's more elegant than a simple hashed map structure? If you have the key, fetching the associated value back out happens in constant time, regardless of the size of the map. The same is true for inserting. In fact, hash maps only become inefficient when you start searching them.
Concetta recently started a new job. Once upon a time, a developer at the office noticed that the user-facing admin pages for their product were garbage. They whipped up their own internal version, which let them accomplish tasks that were difficult, time-consuming, or downright impossible to do in the "official" front end. Time passed, someone noticed, "Hey, this is better than our actual product!", and suddenly the C# code that just lived in one folder on one developer's machine was getting refactored and cleaned up into an application they could release to the public.
Concetta wasn't surprised to see that the code wasn't exactly great. She wasn't surprised that it constantly threw exceptions any time you tried to change anything. But she was surprised by this:
var result = (from kvp in HubProxies where kvp.Key == hubType select kvp.Value).FirstOrDefault();if (result != null) return result;result = hubConnection?.CreateHubProxy(hubType.Name);HubProxies.Add(hubType, result);return result;
HubProxies was a dictionary, mapping Type keys to HubProxy objects. it was pretty clear where the previous developer had stumbled: if a certain value of hubType had never gotten a HubProxy associated with it, you'll get a key error when trying to Get the value there.
Of course, C# dictionaries have a wonderful TryGetValue method, which will accomplish two things: it will get the value and put it in an output parameter without enumerating each individual key, if that key exists, and it will return a boolean telling you whether or not the key exists.
It's the latter part which actually drew Concetta's attention to this block of code: she was getting duplicate key exceptions. This block of code was attempting to add a value for a key which already existed. It's not hard to see why. The FirstOrDefault() line will return either the first match or if there are no matches, null. But what if the dictionary contains nulls?
Concetta's first attempt to fix this code was to use TryGetValue, but that lead to downstream null reference exceptions. As it turned out, the dictionary might contain nulls, but shouldn't contain nulls. It wasn't hard to make sure an actual, concrete value was returned every time. This was no billion dollar mistake, but Concetta was impressed by how much the original developer got wrong in so few lines.
[Advertisement] ProGet can centralize your organization's software applications and components to provide uniform access to developers and servers. Check it out!