HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpMinor

Recursively merge dictionaries with generic types in C#

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
genericmergewithrecursivelytypesdictionaries

Problem

I am looking to implement a method that is capable of merging 2 dictionaries using generics. I've seen several great answers on SO already, but none handle the case of nested dictionaries. As in, what if the value of the dictionaries is another dictionary.

In order to tackle this, I created the following method:

```
using System;
using System.Collections.Generic;
using System.Reflection;

namespace CollectionHelpers
{
///
/// This class contains helpful methods for manipulating collections.
/// Utilizes .NET 4.0 features.
///
public class DictionaryHelper
{
#region Dictionary
///
/// Unionise two dictionaries of generic types.
/// Duplicates take their value from the leftmost dictionary.
///
/// Generic key type
/// Generic value type
/// Dictionary 1
/// Dictionary 2
/// The combined dictionaries.
public static Dictionary UnionDictionaries(Dictionary D1, Dictionary D2)
{
Dictionary rd = new Dictionary(D1);
foreach (var key in D2.Keys)
{
if (!rd.ContainsKey(key))
rd.Add(key, D2[key]);
else if(rd[key].GetType().IsGenericType)
{
if (rd[key].GetType().GetGenericTypeDefinition() == typeof(Dictionary))
{
var mBase = MethodBase.GetCurrentMethod();
MethodInfo info = mBase is MethodInfo ? (MethodInfo)mBase : typeof(DictionaryHelper).GetMethod("UnionDictionaries", BindingFlags.Public | BindingFlags.Static);
var genericMethod = info.MakeGenericMethod(rd[key].GetType().GetGenericArguments()[0], rd[key].GetType().GetGenericArguments()[1]);
var invocationResult = genericMethod.Invoke(null, new object[] { rd[key], D2[key] });
rd[key] = (T2)invocationResult;
}
}
}
return rd;

Solution


  • While using K and V as generic type parameters is an improvement, I would not stop there, and use TKey and TValue instead.



  • I believe the .Net naming convention recommends using camelCase for public method parameters (which you use in your second example, but not in first). Also targetDictionary is way better name then D1 in general, camel case or not.



  • I think this is the case where extension method would make more sense, than regular static method.



  • is operator performs a cast. If you need the result of that cast later on, then you should probably use as instead, and check the result for null. Otherwise you perform the cast twice. I think it is unlikely to become some kind of bottleneck performance-wise, but it's still worth mentioning.



-
In the end, reflection being the obvious downside of the first approach, I would probably go with the second one. Just wrap it with generic method, and you are good, IMHO:

public static Dictionary MergeCollections(Dictionary targetDictionary, Dictionary sourceDictionary)
{
    return (Dictionary)MergeCollections((IDictionary)targetDictionary, (IDictionary)sourceDictionary);
}

private static IDictionary MergeCollections(IDictionary targetDictionary, IDictionary sourceDictionary)
{
    ....
}

Code Snippets

public static Dictionary<TKey, TValue> MergeCollections<TKey, TValue>(Dictionary<TKey, TValue> targetDictionary, Dictionary<TKey, TValue> sourceDictionary)
{
    return (Dictionary<TKey, TValue>)MergeCollections((IDictionary)targetDictionary, (IDictionary)sourceDictionary);
}

private static IDictionary MergeCollections(IDictionary targetDictionary, IDictionary sourceDictionary)
{
    ....
}

Context

StackExchange Code Review Q#94526, answer score: 5

Revisions (0)

No revisions yet.