What's the best way to call a generic method when the type parameter isn't known at compile time, but instead is obtained dynamically at runtime?
Consider the following sample code - inside the Example()
method, what's the most concise way to invoke GenericMethod<T>()
using the Type
stored in the myType
variable?
public class Sample
{
public void Example(string typeName)
{
Type myType = FindType(typeName);
// What goes here to call GenericMethod<T>()?
GenericMethod<myType>(); // This doesn't work
// What changes to call StaticMethod<T>()?
Sample.StaticMethod<myType>(); // This also doesn't work
}
public void GenericMethod<T>()
{
// ...
}
public static void StaticMethod<T>()
{
//...
}
}
This is my 2 cents based on Grax's answer, but with two parameters required for a generic method.
Assume your method is defined as follows in an Helpers class:
In my case, U type is always an observable collection storing object of type T.
As I have my types predefined, I first create the "dummy" objects that represent the observable collection (U) and the object stored in it (T) and that will be used below to get their type when calling the Make
Then call the GetMethod to find your Generic function:
So far, the above call is pretty much identical as to what was explained above but with a small difference when you need have to pass multiple parameters to it.
You need to pass an Type[] array to the MakeGenericMethod function that contains the "dummy" objects' types that were create above:
Once that's done, you need to call the Invoke method as mentioned above.
And you're done. Works a charm!
UPDATE:
As @Bevan highlighted, I do not need to create an array when calling the MakeGenericMethod function as it takes in params and I do not need to create an object in order to get the types as I can just pass the types directly to this function. In my case, since I have the types predefined in another class, I simply changed my code to:
myClassInfo contains 2 properties of type
Type
which I set at run time based on an enum value passed to the constructor and will provide me with the relevant types which I then use in the MakeGenericMethod.Thanks again for highlighting this @Bevan.
With C# 4.0, reflection isn't necessary as the DLR can call it using runtime types. Since using the DLR library is kind of a pain dynamically (instead of the C# compiler generating code for you), the open source framework Dynamitey (.net standard 1.5) gives you easy cached run-time access to the same calls the compiler would generate for you.
Calling a generic method with a type parameter known only at runtime can be greatly simplified by using a
dynamic
type instead of the reflection API.To use this technique the type must be known from the actual object (not just an instance of the
Type
class). Otherwise, you have to create an object of that type or use the standard reflection API solution. You can create an object by using the Activator.CreateInstance method.If you want to call a generic method, that in "normal" usage would have had its type inferred, then it simply comes to casting the object of unknown type to
dynamic
. Here's an example:And here's the output of this program:
Process
is a generic instance method that writes the real type of the passed argument (by using theGetType()
method) and the type of the generic parameter (by usingtypeof
operator).By casting the object argument to
dynamic
type we deferred providing the type parameter until runtime. When theProcess
method is called with thedynamic
argument then the compiler doesn't care about the type of this argument. The compiler generates code that at runtime checks the real types of passed arguments (by using reflection) and choose the best method to call. Here there is only this one generic method, so it's invoked with a proper type parameter.In this example, the output is the same as if you wrote:
The version with a dynamic type is definitely shorter and easier to write. You also shouldn't worry about performance of calling this function multiple times. The next call with arguments of the same type should be faster thanks to the caching mechanism in DLR. Of course, you can write code that cache invoked delegates, but by using the
dynamic
type you get this behaviour for free.If the generic method you want to call don't have an argument of a parametrized type (so its type parameter can't be inferred) then you can wrap the invocation of the generic method in a helper method like in the following example:
Increased type safety
What is really great about using
dynamic
object as a replacement for using reflection API is that you only lose compile time checking of this particular type that you don't know until runtime. Other arguments and the name of the method are staticly analysed by the compiler as usual. If you remove or add more arguments, change their types or rename method name then you'll get a compile-time error. This won't happen if you provide the method name as a string inType.GetMethod
and arguments as the objects array inMethodInfo.Invoke
.Below is a simple example that illustrates how some errors can be caught at compile time (commented code) and other at runtime. It also shows how the DLR tries to resolve which method to call.
Here we again execute some method by casting the argument to the
dynamic
type. Only verification of first argument's type is postponed to runtime. You will get a compiler error if the name of the method you're calling doesn't exist or if other arguments are invalid (wrong number of arguments or wrong types).When you pass the
dynamic
argument to a method then this call is lately bound. Method overload resolution happens at runtime and tries to choose the best overload. So if you invoke theProcessItem
method with an object ofBarItem
type then you'll actually call the non-generic method, because it is a better match for this type. However, you'll get a runtime error when you pass an argument of theAlpha
type because there's no method that can handle this object (a generic method has the constraintwhere T : IItem
andAlpha
class doesn't implement this interface). But that's the whole point. The compiler doesn't have information that this call is valid. You as a programmer know this, and you should make sure that this code runs without errors.Return type gotcha
When you're calling a non-void method with a parameter of dynamic type, its return type will probably be
dynamic
too. So if you'd change previous example to this code:then the type of the result object would be
dynamic
. This is because the compiler don't always know which method will be called. If you know the return type of the function call then you should implicitly convert it to the required type so the rest of the code is statically typed:You'll get a runtime error if the type doesn't match.
Actually, if you try to get the result value in the previous example then you'll get a runtime error in the second loop iteration. This is because you tried to save the return value of a void function.
Nobody provided the "classic Reflection" solution, so here is a complete code example:
The above
DynamicDictionaryFactory
class has a methodCreateDynamicGenericInstance(Type keyType, Type valueType)
and it creates and returns an IDictionary instance, the types of whose keys and values are exactly the specified on the call
keyType
andvalueType
.Here is a complete example how to call this method to instantiate and use a
Dictionary<String, int>
:When the above console application is executed, we get the correct, expected result:
You need to use reflection to get the method to start with, then "construct" it by supplying type arguments with MakeGenericMethod:
For a static method, pass
null
as the first argument toInvoke
. That's nothing to do with generic methods - it's just normal reflection.As noted, a lot of this is simpler as of C# 4 using
dynamic
- if you can use type inference, of course. It doesn't help in cases where type inference isn't available, such as the exact example in the question.Just an addition to the original answer. While this will work:
It is also a little dangerous in that you lose compile-time check for
GenericMethod
. If you later do a refactoring and renameGenericMethod
, this code won't notice and will fail at run time. Also, if there is any post-processing of the assembly (for example obfuscating or removing unused methods/classes) this code might break too.So, if you know the method you are linking to at compile time, and this isn't called millions of times so overhead doesn't matter, I would change this code to be:
While not very pretty, you have a compile time reference to
GenericMethod
here, and if you refactor, delete or do anything withGenericMethod
, this code will keep working, or at least break at compile time (if for example you removeGenericMethod
).Other way to do the same would be to create a new wrapper class, and create it through
Activator
. I don't know if there is a better way.