Is there a more elegant way of adding an item to a Dictionary<> safely?

C#CollectionsDictionary

C# Problem Overview


I need to add key/object pairs to a dictionary, but I of course need to first check if the key already exists otherwise I get a "key already exists in dictionary" error. The code below solves this but is clunky.

What is a more elegant way of doing this without making a string helper method like this?

using System;
using System.Collections.Generic;

namespace TestDictStringObject
{
    class Program
    {
        static void Main(string[] args)
        {
            Dictionary<string, object> currentViews = new Dictionary<string, object>();

            StringHelpers.SafeDictionaryAdd(currentViews, "Customers", "view1");
            StringHelpers.SafeDictionaryAdd(currentViews, "Customers", "view2");
            StringHelpers.SafeDictionaryAdd(currentViews, "Employees", "view1");
            StringHelpers.SafeDictionaryAdd(currentViews, "Reports", "view1");

            foreach (KeyValuePair<string, object> pair in currentViews)
            {
                Console.WriteLine("{0} {1}", pair.Key, pair.Value);
            }
            Console.ReadLine();
        }
    }

    public static class StringHelpers
    {
        public static void SafeDictionaryAdd(Dictionary<string, object> dict, string key, object view)
        {
            if (!dict.ContainsKey(key))
            {
                dict.Add(key, view);
            }
            else
            {
                dict[key] = view;
            }
        }
    }
}

C# Solutions


Solution 1 - C#

Just use the indexer - it will overwrite if it's already there, but it doesn't have to be there first:

Dictionary<string, object> currentViews = new Dictionary<string, object>();
currentViews["Customers"] = "view1";
currentViews["Customers"] = "view2";
currentViews["Employees"] = "view1";
currentViews["Reports"] = "view1";

Basically use Add if the existence of the key indicates a bug (so you want it to throw) and the indexer otherwise. (It's a bit like the difference between casting and using as for reference conversions.)

If you're using C# 3 and you have a distinct set of keys, you can make this even neater:

var currentViews = new Dictionary<string, object>()
{
    { "Customers", "view2" },
    { "Employees", "view1" },
    { "Reports", "view1" },
};

That won't work in your case though, as collection initializers always use Add which will throw on the second Customers entry.

Solution 2 - C#

What's wrong with...

dict[key] = view;

It'll automatically add the key if it's non-existent.

Solution 3 - C#

simply

dict[key] = view;

From the MSDN documentation of Dictionary.Item

> The value associated with the > specified key. If the specified key is > not found, a get operation throws a > KeyNotFoundException, and a set > operation creates a new element with > the specified key.

My emphasis

Solution 4 - C#

As usual John Skeet gets in there with lighting speed with the right answer, but interestingly you could also have written your SafeAdd as an Extension Method on IDictionary.

public static void SafeAdd(this IDictionary<K, T>. dict, K key, T value)...

Solution 5 - C#

Although using the indexer is clearly the right answer for your specific problem, another more general answer to the problem of adding additional functionality to an existing type would be to define an extension method.

Obviously this isn't a particularly useful example, but something to bear in mind for the next time you find a real need:

public static class DictionaryExtensions
{
    public static void SafeAdd<TKey, TValue>(this Dictionary<TKey, TValue> dict, 
                                             TKey key, TValue value)
    {
        dict[key] = value;
    }
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionEdward TanguayView Question on Stackoverflow
Solution 1 - C#Jon SkeetView Answer on Stackoverflow
Solution 2 - C#mmxView Answer on Stackoverflow
Solution 3 - C#Steve GilhamView Answer on Stackoverflow
Solution 4 - C#rohancraggView Answer on Stackoverflow
Solution 5 - C#Daniel EarwickerView Answer on Stackoverflow