ErikE
ErikE

Reputation: 50241

Determine if a type is convertible to IDictionary<string, object>, where Key can be any type

Let's say we have a type that implements IDictionary<string, string>. It's called MyStringDictionary.

I'm doing some property/field/method reflection and would like to know if the member is a type that I can convert to an IDictionary<string, object>.

I know that typeof(IDictionary<string, string>).IsAssignableFrom(typeof(MyStringDictionary)) will be true since both generic parameters match. However, I won't be doing direct assignment to <string, string> but instead will be converting to <string, object>, something like this:

public class MyStringDictionary : IDictionary<string, string> {
   // Notice that the class itself has no generic type arguments!
}

MyStringDictionary myStringDict = GetBigDictionary();
IDictionary<string, object> genericDict = myStringDict
   .ToDictionary(kvp => kvp.Key, kvp => (object) kvp.Value);

How can I determine that this conversion is possible?

I was thinking that I can look to see if it implements IEnumerable<KeyValuePair<,>>, but again I'm stymied by the fact that I don't know the type argument of the Value, and don't need to know it because it will just be boxed to object.

Upvotes: 2

Views: 229

Answers (1)

Sergey Kalinichenko
Sergey Kalinichenko

Reputation: 726639

I was thinking that I can look to see if it implements IEnumerable<KeyValuePair<,>>

Absolutely, that's the way to go! Now for the details of how this can be done: go through results of myObj.GetType().GetInterfaces(), and call the method below. If you get true back, second and third parameters would be set to the type of key and the type of value.

private static bool IsEnumKvp(Type t, out Type k, out Type v) {
    k = v = null;
    if (!t.IsGenericType) return false;
    var genDef = t.GetGenericTypeDefinition();
    if (genDef != typeof(IEnumerable<>)) return false;
    var itemType = t.GenericTypeArguments[0];
    if (!itemType.isGenericType) return false;
    var genItemDef = itemType.GetGenericTypeDefinition();
    if (genItemDef != typeof(KeyValuePair<,>)) return false;
    var kvpTypeArgs = genItemDef.GenericTypeArguments;
    k = kvpTypeArgs[0];
    v = kvpTypeArgs[1];
    return true;
}

Calling this method on MyStringDictionary should produce a true:

foreach (var t : MyStringDictionary.GetType().GetInterfaces()) {
    Type keyType, valType;
    if (IsEnumKvp(t, out keyType, out valType)) {
        Console.WriteLine(
            "Your class implements IEnumerable<KeyValuePair<{0},{1}>>"
        ,   keyType.FullName
        ,   valType.FullName
        );
    }
}

Note that potentially this method could return true multiple types.

Upvotes: 2

Related Questions