Search code examples
c#dictionarygenerics.net-coreordereddictionary

c# generic Merge Method for Dictionaries and OrderedDictionaries


I create the following helper method for merging Dictionaries (overwriting repeating values):

public static IDictionary<T1, T2> Merge<T1, T2>(
    IDictionary<T1, T2> source, IDictionary<T1, T2> additional
) {
    var result = new Dictionary<T1, T2>(source);
    additional.ToList().ForEach(pair => result[pair.Key] = pair.Value);
    return result;
}

I'd like to make it generic so that:

  • I can use it with OrderedDictionary
  • Return the same type of dictionary as source

Any idea how to achieve it or any other improvement to my function?


Solution

  • I can think of a few ways ( none of them really great )

    1) If you don't mind modifying the source dictionary, just add to it and return it, instead of doing new Dictionary

    2) If the caller knows the concrete dictionary type, you can call the generic method explicitly and tell it what type to return or:

    3) You can use a generic class on the concrete type

    4) Instead of using the dictionary classes directly, derive from them and make them ICloneable. Then use clone instead of new dictionary

    EDIT:

    For option #2, I discovered that you don't really need explicit generic calls, explicit casts will be enough. You could have code like this:

    namespace ConsoleApp1
    {
        class Program
        {
            public static T Merge<T>( T source, IDictionary additional) where T:IDictionary,new()
            {
                var result = new T();
                foreach (var item in additional.Keys)
                {
                    result.Add(item, additional[item]);
                }
                foreach (var item in source.Keys)
                {
                    result.Add(item, source[item]);
                }
                return result;
            }
    
            static void Main(string[] args)
            {
                OrderedDictionary od = new OrderedDictionary();
                od["A"] = 1;
    
                System.Collections.Generic.Dictionary<String, Int32> dictionary = new System.Collections.Generic.Dictionary<String, Int32>();
                dictionary["B"] = 2;
                IDictionary od2 = Merge(od, dictionary);
                Console.WriteLine("output=" + od2["A"]+od2["B"]+od2.GetType());
                IDictionary dictionary2 = Merge(dictionary, od);
                Console.WriteLine("output=" + dictionary2["A"] + dictionary2["B"] + dictionary2.GetType());
                Console.ReadKey();
            }
        }
    }
    

    Here are some notes:

    1) You have to use the non-generic System.Collections.IDictionary as the parameter type. Its the common one to Dictionary and OrderedDictionary because OrderedDictionary is not a generic type

    2) To do a new on T,and force it to only be IDictionaries you have to add the where clause

    3) If you don't have the variables for the two inputs declared as concrete types, but only have their interfaces, things get ugly, and then you need explicit casting ( shown below ). In this case , it may be better to chose option #5 and just have the caller pass the target container they would like as a third parameter

        IDictionary od = new OrderedDictionary();
        od["A"] = 1;
        System.Collections.Generic.IDictionary<String, Int32> dictionary = new System.Collections.Generic.Dictionary<String, Int32>();
        dictionary["B"] = 2;
        IDictionary od2 = Merge((OrderedDictionary)od, (System.Collections.Generic.Dictionary<String, Int32>) dictionary);
        Console.WriteLine("output=" + od2["A"]+od2["B"]+od2.GetType());
        IDictionary dictionary2 = Merge((System.Collections.Generic.Dictionary<String, Int32>)dictionary, od);
        Console.WriteLine("output=" + dictionary2["A"] + dictionary2["B"] + dictionary2.GetType());
        Console.ReadKey();