patterncsharpMinor
Recursively merge dictionaries with generic types in C#
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;
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
KandVas generic type parameters is an improvement, I would not stop there, and useTKeyandTValueinstead.
- I believe the .Net naming convention recommends using
camelCasefor public method parameters (which you use in your second example, but not in first). AlsotargetDictionaryis way better name thenD1in general, camel case or not.
- I think this is the case where extension method would make more sense, than regular static method.
isoperator performs a cast. If you need the result of that cast later on, then you should probably useasinstead, 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.