Categories
.net c# generics reflection

How do I use reflection to call a generic method?

1227

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>()
    {
        //...
    }
}

4

  • 8

    I tried Jon’s solution and could not get it to work until I made the generic method public in my class. I know that another Jon replied saying that you need to specify the bindingflags but this did not help.

    – naskew

    Jun 14, 2012 at 7:31

  • 13

    You also need BindingFlags.Instance, not just BindingFlags.NonPublic, to get the private/internal method.

    Feb 15, 2013 at 19:11

  • 2

    Modern version of this question: stackoverflow.com/q/2433436/103167

    – Ben Voigt

    Sep 12, 2014 at 16:38

  • @Peter Mortensen – fyi I used spaces before the ‘?’ to separate the English parts from the non-English (C#) parts; IMHO removing the space makes it look like the ? is part of the code. If there was no code, I’d certainly agree with removing the spaces, but in this case …

    – Bevan

    Nov 28, 2015 at 20:09

1285

You need to use reflection to get the method to start with, then “construct” it by supplying type arguments with MakeGenericMethod:

MethodInfo method = typeof(Sample).GetMethod(nameof(Sample.GenericMethod));
MethodInfo generic = method.MakeGenericMethod(myType);
generic.Invoke(this, null);

For a static method, pass null as the first argument to Invoke. 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.

19

  • 112

    +1; note that GetMethod() only considers public instance methods by default, so you may need BindingFlags.Static and/or BindingFlags.NonPublic.

    – user565869

    Dec 16, 2011 at 22:32

  • 24

    The correct combination of flags is BindingFlags.NonPublic | BindingFlags.Instance (and optionally BindingFlags.Static).

    Feb 15, 2013 at 19:10

  • 5

    A question getting marked dupe of this wonders how to do this with static methods – and technically so does the question here. generic.Invoke()’s first parameter should be null when calling static methods. The first parameter is only necessary when calling instance methods.

    Mar 22, 2013 at 21:30

  • 2

    @ChrisMoschini: Added that to the answer.

    – Jon Skeet

    Mar 22, 2013 at 21:32

  • 2

    @gzou: I’ve added something to the answer – but note that for calling the generic methods in the question, dynamic doesn’t help because type inference isn’t available. (There are no arguments the compiler can use to determine the type argument.)

    – Jon Skeet

    Apr 24, 2015 at 9:44

188

Just an addition to the original answer. While this will work:

MethodInfo method = typeof(Sample).GetMethod("GenericMethod");
MethodInfo generic = method.MakeGenericMethod(myType);
generic.Invoke(this, null);

It is also a little dangerous in that you lose compile-time check for GenericMethod. If you later do a refactoring and rename GenericMethod, 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:

Action<> GenMethod = GenericMethod<int>;  //change int by any base type 
                                          //accepted by GenericMethod
MethodInfo method = this.GetType().GetMethod(GenMethod.Method.Name);
MethodInfo generic = method.MakeGenericMethod(myType);
generic.Invoke(this, null);

While not very pretty, you have a compile time reference to GenericMethod here, and if you refactor, delete or do anything with GenericMethod, this code will keep working, or at least break at compile time (if for example you remove GenericMethod).

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.

8

  • 5

    In cases where reflection is used to call a method, it’s usual that the method name is itself discovered by another method. Knowing the method name in advance isn’t common.

    – Bevan

    Feb 27, 2011 at 21:59

  • 15

    Well, I agree for common uses of reflection. But the original question was how to call “GenericMethod<myType>()” If that syntax was allowed, we wouldn’t need GetMethod() at all. But for the question “how do I write “GenericMethod<myType>”? I think the answer should include a way to avoid losing the compile-time link with GenericMethod. Now if this question is common or not I don’t know, but I do know I had this exact problem yesterday, and that’s why I landed in this question.

    Feb 28, 2011 at 21:24


  • 20

    You could do GenMethod.Method.GetGenericMethodDefinition() instead of this.GetType().GetMethod(GenMethod.Method.Name). It’s slightly cleaner and probably safer.

    May 10, 2011 at 10:10

  • 41

    Now you can use nameof(GenericMethod)

    – dmigo

    Mar 17, 2016 at 10:02

  • 1

    @EricScherrer: It should be Action not Action<>

    – Ben Voigt

    Jul 14 at 17:59

166

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:

class Alpha { }
class Beta { }
class Service
{
    public void Process<T>(T item)
    {
        Console.WriteLine("item.GetType(): " + item.GetType()
                          + "\ttypeof(T): " + typeof(T));
    }
}

class Program
{
    static void Main(string[] args)
    {
        var a = new Alpha();
        var b = new Beta();

        var service = new Service();
        service.Process(a); // Same as "service.Process<Alpha>(a)"
        service.Process(b); // Same as "service.Process<Beta>(b)"

        var objects = new object[] { a, b };
        foreach (var o in objects)
        {
            service.Process(o); // Same as "service.Process<object>(o)"
        }
        foreach (var o in objects)
        {
            dynamic dynObj = o;
            service.Process(dynObj); // Or write "service.Process((dynamic)o)"
        }
    }
}

And here’s the output of this program:

item.GetType(): Alpha    typeof(T): Alpha
item.GetType(): Beta     typeof(T): Beta
item.GetType(): Alpha    typeof(T): System.Object
item.GetType(): Beta     typeof(T): System.Object
item.GetType(): Alpha    typeof(T): Alpha
item.GetType(): Beta     typeof(T): Beta

Process is a generic instance method that writes the real type of the passed argument (by using the GetType() method) and the type of the generic parameter (by using typeof operator).

By casting the object argument to dynamic type we deferred providing the type parameter until runtime. When the Process method is called with the dynamic 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:

foreach (var o in objects)
{
    MethodInfo method = typeof(Service).GetMethod("Process");
    MethodInfo generic = method.MakeGenericMethod(o.GetType());
    generic.Invoke(service, new object[] { o });
}

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:

class Program
{
    static void Main(string[] args)
    {
        object obj = new Alpha();

        Helper((dynamic)obj);
    }

    public static void Helper<T>(T obj)
    {
        GenericMethod<T>();
    }

    public static void GenericMethod<T>()
    {
        Console.WriteLine("GenericMethod<" + typeof(T) + ">");
    }
}

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 in Type.GetMethod and arguments as the objects array in MethodInfo.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.

interface IItem { }
class FooItem : IItem { }
class BarItem : IItem { }
class Alpha { }

class Program
{
    static void Main(string[] args)
    {
        var objects = new object[] { new FooItem(), new BarItem(), new Alpha() };
        for (int i = 0; i < objects.Length; i++)
        {
            ProcessItem((dynamic)objects[i], "test" + i, i);

            //ProcesItm((dynamic)objects[i], "test" + i, i);
            //compiler error: The name 'ProcesItm' does not
            //exist in the current context

            //ProcessItem((dynamic)objects[i], "test" + i);
            //error: No overload for method 'ProcessItem' takes 2 arguments
        }
    }

    static string ProcessItem<T>(T item, string text, int number)
        where T : IItem
    {
        Console.WriteLine("Generic ProcessItem<{0}>, text {1}, number:{2}",
                          typeof(T), text, number);
        return "OK";
    }
    static void ProcessItem(BarItem item, string text, int number)
    {
        Console.WriteLine("ProcessItem with Bar, " + text + ", " + number);
    }
}

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 the ProcessItem method with an object of BarItem 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 the Alpha type because there’s no method that can handle this object (a generic method has the constraint where T : IItem and Alpha 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:

var result = ProcessItem((dynamic)testObjects[i], "test" + i, i);

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:

string result = ProcessItem((dynamic)testObjects[i], "test" + i, i);

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.

3

  • Mariusz, confused by “However, you’ll get runtime error when you pass argument of Alpha type because there’s no method that can handle this object. ” If I call var a = new Alpha() ProcessItem(a,”test” + i, i) Why wouldn’t the generic ProcessItem method handle this effectively, outputting “General Process Item”?

    Mar 21, 2015 at 18:51


  • @AlexEdelstein I edited my answer to clarify a bit. It’s because generic ProcessItem method has generic constraint and accepts only object that implements IItem interface. When you will call ProcessItem(new Aplha(), "test" , 1); or ProcessItem((object)(new Aplha()), "test" , 1); you’ll get a compiler error but when casting to dynamic you postpone that check to runtime.

    Mar 23, 2015 at 11:38

  • Great answer and explanation, works perfectly for me. Much better than the accepted answer, shorter to write, more performant, and safer.

    – ygoe

    Aug 28, 2015 at 9:08