Generic type parameters and dynamic types in C#Posted: October 31, 2014
There may be times when you need to use some type in your application that is not known until runtime. This post will explain what you need to do in order to be able to use such a dynamically loaded type from a dynamically loaded assembly as a generic type parameter when you create an instance of some generic class in your application.
It should be clear that a generic class is like a template or blueprint for a type and that a developer that aims to use the class must specify a type inside the angle brackets when creating an instance of the generic class. If you want to learn more about generic types themselves, I suggest you read the chapter on generic type parameters in the C# programming guide on MSDN which is available here.
Now, if you try to create an instance of a generic class, like for example a List<T>, and simply set its type parameter to some Type variable that has been created at runtime, you won’t be able to compile your application.
For example, consider the following (erroneous) code where I try to create a List<T> and set its type argument to a type called “SomeCustomClass” that was defined in a namespace called “SomeCustomNamespace” in a dynamically loaded assembly called “SomeCustomAssembly.dll”:
//load the assembly where the type is defined Assembly assembly = Assembly.LoadFrom(@"C:\Users\magnus\SomeCustomAssembly.dll"); //get the type Type customType = assembly.GetType("SomeCustomNamespace.SomeCustomClass"); /* ! THIS WILL NOT COMPILE ! */ List<customType> objects = new List<customType>();
You cannot do this. After all, the main purpose of generics is to provide compile-time type safety as fixing compile-time errors is usually a lot easier than finding and fixing errors that only occur at runtime.
So how can you create a List<T>, or any other generic type for that matter, that contains objects of a type that is unknown when you compile the application? Luckily, there is a MakeGenericType method in the System.Type class that you can use to create the actual List<SomeCustomClass> type dynamically:
//load the assembly where the type is defined Assembly assembly = Assembly.LoadFrom(@"C:\Users\magnus\SomeCustomAssembly.dll"); //get the type Type customType = assembly.GetType("SomeCustomNamespace.SomeCustomClass"); //create the actual List<customType> type Type genericListType = typeof(List<>); Type customListType = genericListType.MakeGenericType(customType);
You could then use the static System.Activator.CreateInstance method – this method creates an instance of the specified type using that type’s default constructor – to create an instance of the customListType above:
IList customListInstance = (IList)Activator.CreateInstance(customListType);
This will create a List<T> that can only contain objects of the dynamically loaded type. If you call the GetType() method on this list instance during a debug session in Visual Studio, you will see that it is actually a List<SomeCustomClass>:
Since the Activator.CreateInstance returns an object, the result is casted to an IList which is an interface that is implemented by the generic List<T> class. Whenever you want to add some SomeCustomClass item to the list, you simply create an instance of the dynamic type using the Activator.CreateInstance method and add it to the list using the Add method:
object someCustomClassInstance = Activator.CreateInstance(customType); customListInstance.Add(someCustomClassInstance);
Note that if you try to add an object of any other type than SomeCustomClass in this case, it will result in a System.ArgumentException since the customListInstance is really a List<SomeCustomClass> that only accepts SomeCustomClass objects.
It is important to realize that you won’t get any kind of compile-time checking when using a dynamic type like this. For example, the following code would compile just fine but always result in a runtime error when being run since I try to add a string to a List<SomeCustomClass>:
IList customListInstance = (IList)Activator.CreateInstance(customListType); object someCustomClassInstance = Activator.CreateInstance(customType); customListInstance.Add(someCustomClassInstance); string someString = "abc"; /* ! This will result in a System.ArgumentException ! */ customListInstance.Add(someString);
Therefore, you should only use dynamic types when you are required to do so for some reason. If you know exactly which assemblies and types you are going to use beforehand, you should of course add a reference to the assembly and use the types in it as usual. Except for the type safety, there is obviously also some performance penalties associated with creating types and objects dynamically.