kooldeji
kooldeji

Reputation: 59

How to Cast a generic type of any type to another type

I am trying to create a recursive method that makes a deep copy of a dictionary to any length by checking if the hash value is another dictionary and then copy it again before hashing it.

I have tried this and the compiler rejects the line in Blockquote, who can find the error?

private Dictionary<TKey, TValue> NestedCopy<TKey, TValue>(Dictionary<TKey, 
                                                         TValue> nestedDict)
{

    var retDict = new Dictionary<TKey, TValue>();
    foreach (var dict in nestedDict)
    {
         if (dict.Value is Dictionary<Object, Object>)
         {
             retDict[dict.Key] = NestedCopy(dict.Value asDictionary<object, object>);
         }
     }
     return retDict;
}

retDict[dict.Key] = NestedCopy(dict.Value asDictionary);

This is the error line,

It says it cannot implicitly convert from Dictionary to TValue

Dictionary<string, Dictionary<string, int>> dict; 
var newDict = NestedCopy(newDict);
//I expect newDict to be a copy of dict

Upvotes: 0

Views: 93

Answers (2)

grek40
grek40

Reputation: 13438

There is no way for the compiler to statically infer the recursive call. So, you gonna need reflection or at least let the compiler do the reflection for you by using the dynamic keyword:

private Dictionary<TKey, TValue> NestedCopy<TKey, TValue>(
    Dictionary<TKey, TValue> nestedDict)
{
    var retDict = new Dictionary<TKey, TValue>();
    foreach (var dict in nestedDict)
    {
        if (typeof(TValue).IsGenericType && typeof(TValue).GetGenericTypeDefinition() == typeof(Dictionary<,>))
        {
            retDict[dict.Key] = (TValue)NestedCopy((dynamic)dict.Value);
        }
        else
        {
            retDict[dict.Key] = dict.Value;
        }
    }
    return retDict;
}

The more explicit code with hand-made reflection could look as follows:

private static Dictionary<TKey, TValue> NestedCopy<TKey, TValue>(
    Dictionary<TKey, TValue> nestedDict)
{
    var reflectionMethod = typeof(Program).GetMethod("NestedCopy", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static);
    var retDict = new Dictionary<TKey, TValue>();
    foreach (var dict in nestedDict)
    {
        if (typeof(TValue).IsGenericType && typeof(TValue).GetGenericTypeDefinition() == typeof(Dictionary<,>))
        {
            var methodToCall = reflectionMethod.MakeGenericMethod(typeof(TValue).GetGenericArguments());
            retDict[dict.Key] = (TValue)methodToCall.Invoke(null, new object[] { dict.Value });
        }
        else
        {
            retDict[dict.Key] = dict.Value;
        }
    }
    return retDict;
}

Note this assumes the method to belong to Program class and I made it static since its not using any context.

Since the decision for the if-else doesn't depend on dict.Value but only on the TValue that stays the same throughout the method, you could also move the condition out of the loop:

private static Dictionary<TKey, TValue> NestedCopy<TKey, TValue>(
    Dictionary<TKey, TValue> nestedDict)
{
    var retDict = new Dictionary<TKey, TValue>();
    Func<TValue, TValue> clone;
    if (typeof(TValue).IsGenericType && typeof(TValue).GetGenericTypeDefinition() == typeof(Dictionary<,>))
    {
        clone = v => NestedCopy((dynamic)v);
    }
    else
    {
        clone = v => v;
    }
    foreach (var dict in nestedDict)
    {
        retDict[dict.Key] = clone(dict.Value);
    }
    return retDict;
}

Upvotes: 2

jerry
jerry

Reputation: 333

I think you don't need to recursive it, it relies on you how to implement the TValue.Clone method.

private Dictionary<TKey, TValue> CloneDictionary<TKey, TValue>(Dictionary<TKey, TValue> sourceDic)
            where TValue : ICloneable
        {
            var ret = new Dictionary<TKey, TValue>(sourceDic.Count, sourceDic.Comparer);
            foreach (KeyValuePair<TKey, TValue> entry in sourceDic)
            {
                ret.Add(entry.Key, (TValue)entry.Value.Clone());
            }
            return ret;
        }

Upvotes: -1

Related Questions