Sometimes we need to run methods or create types with generic parameters at runtime. For example, situation where the type is not known at compiler time but the method(s) we want to use expect the generic parameter to be supplied.
Let’s take a look at the syntax of various scenarios and you’ll get the idea.
Calling an instance method
So let’s assume we have some code like this
public class Runner { public T Create<T>() { // do something return default(T); } }
and we want to invoke this at runtime with an “unknown” (i.e. discovered at runtime) type. We can write something like this
object unknown = CreateType(); // generates some type at runtime var runner = new Runner(); typeof (Runner). GetMethod("Create"). MakeGenericMethod(unknown.GetType()). Invoke(runner, null);
This code would also work if the Create method was a static.
Note: we can ofcourse use typeof(Runner) or runner.GetType() in the above depending upon your preference or use.
Next up, let’s look at the same code but where we need to also pass the method a generic parameter.
public class Runner { public T Create<T>(T type) { // do something return type; } }
So the only real difference here is that we also need to pass the type into the method, a simple addition of the parameters to the invoke will allow this to work, here’s the code
object unknown = CreateType(); // generates some type at runtime var runner = new Runner(); typeof(Runner). GetMethod("Create"). MakeGenericMethod(unknown.GetType()). Invoke(runner, new []{ unknown });
Calling methods on a static class or extension methods
As you will know, extension methods are really just static classes with syntactic sugar to allow them to appear like instance methods, so the procedure for invoking them is the same as the normal static classed, but we’ll cover examples here all the same.
Let’s first look at a static class
public static class Runner { public static T Create<T>() { // do something return default(T); } }
The only real difference to the code for the instance method on a non-static class is that we do not pass a instance to the first parameter of the invoke method, like this
object unknown = CreateType(); // generates some type at runtime typeof(Runner). GetMethod("Create"). MakeGenericMethod(unknown.GetType()). Invoke(null, null);
As extension methods expect a “this” argument, they’re no different to the above code, expect that we need to ensure the first argument is the instance of an object
public static class Runner { public static T Create<T>(this DoSomething doSomething) { // do something return default(T); } } public class DoSomething { }
So this assumes Create is an extension method for the class, DoSomething. To invoke this Create method we can simply write
object unknown = CreateType(); // generates some type at runtime var doSomething = new DoSomething(); typeof(Runner). GetMethod("Create"). MakeGenericMethod(unknown.GetType()). Invoke(null, new object[]{ doSomething });
Classes with generic parameters
So now let’s move the generic parameter onto the class itself. We’ll begin by looking at static classes
public static class Runner<T> { public static T Create() { // do something return default(T); } }
So now we need to make the generic on the type not the method. We get this
typeof(Runner<>). MakeGenericType(unknown.GetType()). GetMethod("Create"). Invoke(null, null);
Notice how the MakeGenericType is used to generate our generic class and the syntax of the typeof.
Obviously we might wish to create non-static classes with generic parameters as well, something like this
public class Runner<T> { public T Create() { // do something return default(T); } }
ofcourse we can’t simply create an instance to this class and use the same techniques of previous because the type of the generic parameter is not known, so we need to create an instance of this class via reflection then invoke the method – this can be accomplished with
var genericType = typeof (Runner<>). MakeGenericType(unknown.GetType()); var runner = Activator.CreateInstance(genericType); genericType .GetMethod("Create"). Invoke(runner, null);
In the above we need to use the genericType twice, so we store it in a local variable. The first time we use it is with Activator.CreateInstance this creates an instance of the class with a generic parameter. Next we use the same type but with the GetMethod call and ofcourse pass the instance variable into Invoke.
What about when we have more than one generic parameter
So what if we had something like this
public class Runner<T1, T2> { public T1 Create() { var t2 = default(T2); // do something return default(T1); } }
Obviously this is assuming T2 actually does something of use in our code.
All of the previous sample code will work, the difference is that we declare the typeof(Runner<>) as typeof(Runner<,>) and need to pass the extra parameters in the MakeGenericType method, for example
var genericType = typeof (Runner<,>). MakeGenericType(unknown1.GetType(), unknown2.GetType()); var runner = Activator.CreateInstance(genericType); genericType .GetMethod("Create"). Invoke(runner, null);