I learned something new today. Not uncommon, but it's a core .NET runtime feature I simply did not know although I know I've run into this issue a few times and worked around it in other ways. Today there was no working around it and a few folks on Twitter pointed me in the right direction.
The question I ran into is:
How do I create a type instance of a generic type when I have dynamically acquired the type at runtime?
Yup it's not something that you do everyday, but when you're writing code that parses objects dynamically at runtime it comes up from time to time. In my case it's in the bowels of a custom JSON parser. After some thought triggered by a comment today I realized it would be fairly easy to implement two-way Dictionary parsing for most concrete dictionary types. I could use a custom Dictionary serialization format that serializes as an array of key/value objects. Basically I can use a custom type (that matches the JSON signature) to hold my parsed dictionary data and then add it to the actual dictionary when parsing is complete.
Figuring out Generic Parameters of a Type at Runtime
One issue that came up in the process was how to figure out what type the Dictionary<K,V> generic parameters take. For the following code assume that arrayType is a known type instance of an array-like object - an IList, IDictionary or a plain array. In this case I'm looking specifically for types that implement IDictionary.
Reflection actually makes it fairly easy to figure out generic parameter types (ie. what concrete types K and V are) at runtime with code like this:
if (arrayType.GetInterface("IDictionary") != null)
{
if (arrayType.IsGenericType)
{
var keyType = arrayType.GetGenericArguments()[0];
var valueType = arrayType.GetGenericArguments()[1];
…
}
}
The GetArrayType method gets passed a type instance that is the array or array-like object that is rendered in JSON as an array (which includes IList, IDictionary, IDataReader and a few others). In my case the type passed would be something like Dictionary<string, CustomerEntity>. So I know what the parent container class type (the IDictionary type) is. Based on the the container type using it's then possible to use GetGenericTypeArguments() to retrieve all the generic types in sequential order of definition (ie. string, CustomerEntity).
That's the easy part.
Creating a Generic Type and Providing Generic Parameters at RunTime
The next problem is how do I get a concrete type instance for the generic type? I know what the type name and I have a type instance is but it's generic, so how do I get a type reference to keyvaluepair<K,V> that is specific to the keyType and valueType above?
Here are a couple of things that come to mind but that don't work (and yes I tried that unsuccessfully first):
Type elementType = typeof(keyvalue<keyType, valueType>);
Type elementType = typeof(keyvalue<typeof(keyType), typeof(valueType)>);
The problem is that this explicit syntax expects a type literal not some dynamic runtime value, so both of the above won't even compile.
I turns out the way to create a generic type at runtime is using a fancy bit of syntax that until today I was completely unaware of:
Type elementType = typeof(keyvalue<,>).MakeGenericType(keyType, valueType);
The key is the type(keyvalue<,>) bit which looks weird at best. It works however and produces a non-generic type reference. You can see the difference between the full generic type and the non-typed (?) generic type in the debugger:
The nonGenericType doesn't show any type specialization, while the elementType type shows the string, CustomerEntity (truncated above) in the type name.
Once the full type reference exists (elementType) it's then easy to create an instance of the element type.
// Objects start out null until we find the opening tag
resultObject = Activator.CreateInstance(elementType);
In my case the parser parses through the JSON and when it completes parsing the value/object it creates a new keyvalue<T,V> instance. keyvalue<T,V> is a custom type I created that only contains key, value properties that match the JSON signature of the JSON serializer exactly so when the object is deserialized the signature matches and it just all works using the stock object deserialization. I use a List<keyvalue<T,V>> to hold these items as they are parsed and only when done parsing do I turn that list into the proper kind of dictionary. This way the parsing code works essentially the same regardless of the type of list interface used.
Parsing through a Generic type when you only have Runtime Type Information
This brings up yet another generic type issue. At the end of the parsing sequence I now have a List<> of a generic items.
When parsing of the JSON array is done, the List needs to be turned into a defacto Dictionary<K,V>. This should be easy since I know that I'm dealing with an IDictionary, and I know the generic types for the key and value. But now I need to call dict.Add(key,value) and both key and value need to be of the proper type for these calls to succeed. Even though my elements are of the correct type, the compiler doesn't know it because the type was created dynamically.
One - ugly - way to do this would be to use Type.ConvertType() to convert both the key and value types.
In the end I decided the easier and probably only slightly slower way to do this is a to use the dynamic type to collect the items and assign them to avoid all the dynamic casting madness:
else if (IsIDictionary)
{
IDictionary dict = Activator.CreateInstance(arrayType) as IDictionary;
foreach (dynamic item in items)
{
dict.Add(item.key, item.value);
}
return dict;
}
This code creates an instance of the final generic dictionary type first, then loops through all of my custom keyvalue<K,V> items and assigns them to the actual dictionary. By using dynamic here I can side step all the explicit type conversions that would be required in the three highlighted areas (not to mention that this nested method doesn't have access to the dictionary item generic types here). Dynamic makes this code a lot cleaner than it would have otherwise been.
Static <- -> Dynamic
Dynamic casting in a static language like C# is a bitch to say the least. This is one of the few times when I've cursed static typing and the arcane syntax that's required to coax types into the right format. It works but it's pretty nasty code. If it weren't for dynamic that last bit of code would have been a pretty ugly as well with a bunch of Convert.ChangeType() calls to litter the code.
Fortunately this type of type convulsion is rather rare and reserved for system level code. It's not every day that you create a string to object parser after all :-)
Other Posts you might also like