Tuesday, January 8, 2013

Improving the Generic C# Dictionary with extension methods

The Generic Dictionary in C#/.NET is very versatile and I use it a lot. So often that I found myself writing the same pattern of code around them over and over again. Code I feel should be a part of the class itself. Therefore I've taken the liberty of writing a couple of short extension methods that I really think would help clean up and make your code more readable whenever you're using Dictionaries. Speaking of readability - It's important to understand your own code.

The first pattern I often encounter, is checking whether or not a key exists in the dictionary before attempting to retrieve the value. As we know, the dictionary throws an exception if the key does not exist. Yes, I know there is TryGetValue-method, but then you have to meddle with out-parameters and your get-code is already more than one line. The extension method GetOrDefault below aims at solving this. Instead of throwing an exception when the key is not present, it returns the default value of that class.

The second pattern I often use is also related to non-existent keys. But this time I want to construct a new object if it doesn't exist, and work with this new object in the code lines to come. There is an elegant solution to this as well using generics, although it will require the value type to be of a class type. See method GetOrInsertNew below on how I solved that in four lines.

Here is the complete class:

public static class DictionaryExtensions
    {
        /// <summary>
        /// Returns the default value of type U if the key does not exist in the dictionary
        /// </summary>
        public static U GetOrDefault<T, U>(this Dictionary<T, U> dic, T key)
        {
            if (dic.ContainsKey(key)) return dic[key];
            return default(U);
        }

        /// <summary>
        /// Returns an existing value U for key T, or creates a new instance of type U using the default constructor, 
        /// adds it to the dictionary and returns it.
        /// </summary>
        public static U GetOrInsertNew<T, U>(this Dictionary<T, U> dic, T key)
            where U : new()
        {
            if (dic.ContainsKey(key)) return dic[key];
            U newObj = new U();
            dic[key] = newObj;
            return newObj;
        }
    }

As an example of how using this improves the readability of your code, let's say you're building some kind of custom session handling for some sort of service. Using plain Dictionary without the extension, you could implement the GetSession-method like this:

private Dictionary<string, Session> _sessionDic; 

    public Session GetSession(string sessionId)
    {
        if (_sessionDic.ContainsKey(sessionId)) return _sessionDic[sessionId];
        var session = new Session();
        _sessionDic[sessionId] = session;
        return session;
    }      

Now using the extension methods, the retrieving code could be reduced to one line:

private Dictionary<string, Session> _sessionDic; 

    public Session GetSession(string sessionId)
    {
        return _sessionDic.GetOrInsertNew(sessionId);
    }

Another convenience is that if you are like me and work with other languages that are dynamically typed, like Javascript or PHP, the Dictionary will now behave more like their counterparts Associative arrays in Javascript and Arrays in PHP. (Yes I know they're far from exactly the same, but they have comparable behaviour)

In summary, this is no sorcery - just a simple tool that I have found to be nice to have in my toolbox, and what I believe to be a good application of extension methods.


Update 11th Feb 2013:

Simen's suggestion from the comments is implemented and posted to this public github gist

4 comments:

  1. Nice extensions, but I think you can improve them a bit: GetOrDefault should be able to take a custom default value, and TryGetValue should be used.

    I think TryGetValue will be faster in release mode as only *one* lookup is performed instead of two as in ContainsKey + getvalue (but I might be wrong).

    public static V GetOrDefault(this Dictionary dict, K key, V onMissing = default(V))
    {
    V value;
    return dict.TryGetValue(key, out value) ? value : onMissing;
    }

    ReplyDelete
    Replies
    1. Pre tag not allowed and < and > wasn't rendered correcly. Here it is again:

      public static V GetOrDefault<K, V>(this Dictionary<K, V> dict, K key, V onMissing = default(V))
      {
      V value;
      return dict.TryGetValue(key, out value) ? value : onMissing;
      }

      Delete
  2. Thank's Simen that is definitely an improvement! Have posted the updated code to github and linked to it from the bottom of the post.

    ReplyDelete
  3. Thanks for this.

    I was working on a similar idea for a ValueOrDefault() extension method when I came across this while trying to clarify the generics syntax. I implemented that as GetOrDefault() along with a variation on your GetOrInsertNew() method.

    This evening while working on somewhere else I had a different need and implemented the GetOrInsert() method shown below. Rather than using a default constructor to create a new value it calls a Func that you pass in. This also means it can be used on dictionaries that have value types or do not support a default constructor.

    public static TValue GetOrInsert(this Dictionary dict, TKey key, Func createNewValue)
    {
    TValue value;

    if (!dict.TryGetValue(key, out value))
    {
    value = createNewValue();
    dict[key] = value;
    }

    return value;
    }

    In the code I was working on I called it as follows;

    var accounts = new Dictionary();
    foreach (var transaction in transactions)
    {
    var account = accounts.GetOrInsert(transaction.Account, () => BudgetAccount.Parse(transaction.Account));
    }

    ReplyDelete