Click here to Skip to main content
15,867,851 members
Articles / Programming Languages / C#
Article

Dynamically Invoke Generic Methods

Rate me:
Please Sign up or sign in to vote.
5.00/5 (19 votes)
1 Jan 2008GPL32 min read 100.3K   1K   56   12
Reflection on generics can be complicated. This article shows how to use the DynamicMethod class to dynamically invoke a generic method.

Introduction

A few days ago, I needed to dynamically invoke a method on a class. Saying only this, things appear very simple.
As usual it got complicated... I figured out that the method was a generic one, and that the class had several overloads defined for the given method.

Background

When embracing this new task, I was obviously concerned with the performance output of the solution, and that's why I opted for the DynamicMethod class (new in .NET Framework 2.0) and Reflection Emit.
There are several pages on the Web that compare the DynamicMethod approach with the Reflection Invoke one, with a very large performance boost for the first one (please read this MSDN magazine article).

To use the DynamicMethod class approach, one must be able to generate the IL code for the method. I have some knowledge on this subject, but you all can do it. Simply code your method in C# or another .NET language, compile it, and then use Lutz Roeder's .NET Reflector to look at the generated IL. With a little persistence you will get the job done.

The Solution

The development output is a simple static class with a few public methods that allow you to create a GenericInvoker for any generic method.

GenericInvoker is a delegate defined as follows.

Note: On methods that have no return value, a call to the delegate will always return null.

C#
public delegate object GenericInvoker(object target, params object[] arguments);

You can create an instance of the GenericInvoker delegate by simply calling one of the overrides for the GenericMethodInvokerMethod method on the DynamiMethods static class (included in the article source code archive).

Note: The GenericInvoker delegate creation can be a slow process. Therefore if you are going to use it in some kind of loop, you should consider caching the value to reduce the performance impact.

Using the Code

Here is a simple example on how to use the supplied DynamicMethods class:

C#
// sample class
public class SampleClass {
  private string instanceName;

  public SampleClass(string instanceName) {
    this.instanceName = instanceName;
  }

  public void Test<TType>(TType s) {
    MessageBox.Show(string.Format("{0} From {1}", s, this.instanceName));
  }

  public string Concatenate<TType>(TType lhs, TType rhs) {
    return string.Format("{0}{1}", lhs, rhs);
  }

  public string Concatenate<TType>(string prefix, TType lhs, TType rhs) {
    return string.Format("{0} - {1}{2}", prefix, lhs, rhs);
  }
}

// Tests class
public class Tests {
  public void Tests() {

    SampleClass instance = new SampleClass("Instance 1");
    GenericInvoker invoker;

    // invoke method that returns void
    invoker = DynamicMethods.GenericMethodInvokerMethod(typeof(SampleClass), "Test", 
        new Type[] { typeof(string) });
    ShowResult(invoker(instance, "this is a tests"));

    // invoke method that returns string, the parameter types are used to find 
    // the correct overload
    invoker = DynamicMethods.GenericMethodInvokerMethod(typeof(SampleClass), 
        "Concatenate", new Type[] { typeof(int) },
      new Type[] { typeof(int), typeof(int) });
    ShowResult(invoker(instance, 100, 200));

    // invoke method that returns string, the parameter types are used to find 
    // the correct overload
    invoker = DynamicMethods.GenericMethodInvokerMethod(typeof(SampleClass), 
        "Concatenate", new Type[] { typeof(int) },
      new Type[] { typeof(string), typeof(int), typeof(int) });
    ShowResult(invoker(instance, "PREFIX", 100, 200));
  }

  // show GenericInvoker result
  private static void ShowResult(object result) {
    if (null == result) {
      MessageBox.Show("return is null");
    } else {
      MessageBox.Show(string.Format("return is {0}", result));
    }
  }
}

Points of Interest

One of the major problems that I faced was related to the GetMethod method from the Type class.
Although GetMethod works very well with normal type methods, it doesn't do so for generic methods. If the generic method has overloads, than the GetMethod call will always return null.

To overcome this limitation, I had to use the GetMethods method and iterate through all the type methods to get the correct one. Here is the code that gets the job done:

C#
private static void FindMethod(Type type, string methodName, Type[] typeArguments, 
        Type[] parameterTypes, out MethodInfo methodInfo,
  out ParameterInfo[] parameters) {

  methodInfo = null;
  parameters = null;

  if (null == parameterTypes) {
    methodInfo = type.GetMethod(methodName, BindingFlags.Public | BindingFlags.Instance);
    methodInfo = methodInfo.MakeGenericMethod(typeArguments);
    parameters = methodInfo.GetParameters();
  } else {
     // Method is probably overloaded. As far as I know there's no other way 
     // to get the MethodInfo instance, we have to
     // search for it in all the type methods
    MethodInfo[] methods = type.GetMethods(BindingFlags.Public | BindingFlags.Instance);
    foreach (MethodInfo method in methods) {
      if (method.Name == methodName) {
         // create the generic method
        MethodInfo genericMethod = method.MakeGenericMethod(typeArguments);
        parameters = genericMethod.GetParameters();

         // compare the method parameters
        if (parameters.Length == parameterTypes.Length) {
          for (int i = 0; i < parameters.Length; i++) {
            if (parameters[i].ParameterType != parameterTypes[i]) {
              continue; // this is not the method we're looking for
            }
          }

           // if we're here, we got the right method
          methodInfo = genericMethod;
          break;
        }
      }
    }

    if (null == methodInfo) {
      throw new InvalidOperationException("Method not found");
    }
  }
}

History

  • 2007.01.02: Initial release

License

This article, along with any associated source code and files, is licensed under The GNU General Public License (GPLv3)


Written By
Team Leader Critical Software
Portugal Portugal
Been working as a developer and team leader for windows platform projects for almost 10 years

I keep a blog at http://wiggythoughts.blogspot.com/

Comments and Discussions

 
Bugerror in FindMethod Pin
account_for_file_download23-Jul-14 4:06
account_for_file_download23-Jul-14 4:06 
GeneralException when method has out parameters Pin
joni ba27-Oct-10 6:02
joni ba27-Oct-10 6:02 
GeneralMy vote of 5 Pin
Stefan Fork14-Jul-10 20:35
Stefan Fork14-Jul-10 20:35 
Generalparams parameters Pin
garfield_lambda4-Mar-10 8:14
garfield_lambda4-Mar-10 8:14 
GeneralRe: params parameters Pin
Pedro Gomes5-Mar-10 20:17
Pedro Gomes5-Mar-10 20:17 
GeneralInvalidCastException Pin
lobotomy1-May-09 10:18
professionallobotomy1-May-09 10:18 
GeneralRe: InvalidCastException [modified] Pin
Pedro Gomes2-May-09 20:39
Pedro Gomes2-May-09 20:39 
GeneralRe: InvalidCastException Pin
lobotomy3-May-09 3:23
professionallobotomy3-May-09 3:23 
GeneralBeautiful Pin
vekaz4-Mar-09 13:00
vekaz4-Mar-09 13:00 
GeneralStatic classes Pin
Steve H. (UK)6-Mar-08 6:44
Steve H. (UK)6-Mar-08 6:44 
GeneralRe: Static classes Pin
Peregrine13-Aug-08 9:27
Peregrine13-Aug-08 9:27 
1. Use call, not callvirt.
2. Try eliminating the ldarg0 and castclass
GeneralNice Pin
ccache4-Jan-08 6:31
ccache4-Jan-08 6:31 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.