开发者

Retrieving the MethodInfo of of the correct overload of a generic method

开发者 https://www.devze.com 2023-01-31 03:07 出处:网络
I have this type that contains two overloads of a generic method. I like to retrieve one of the overloads (with the Func<T> parameter) using reflection. The problem however is that I can\'t find

I have this type that contains two overloads of a generic method. I like to retrieve one of the overloads (with the Func<T> parameter) using reflection. The problem however is that I can't find the correct parameter type to supply the Type.GetMethod(string, Type[]) method with.

Here is my class definition:

public class Foo
{
    public void Bar<T>(Func<T> f) { }
    public void Bar<T>(Action<T> a) { }
}

And this is what I've come up with, unfortunately without succ开发者_Python百科es:

[TestMethod]
public void Test1()
{
    Type parameterType = typeof(Func<>);

    var method = typeof(Foo).GetMethod("Bar", new Type[] { parameterType });

    Assert.IsNotNull(method); // Fails
}

How can I get the MethodInfo of a generic method of which I know the parameters?


Why don't you use expression trees? This makes it much easier:

public static MethodInfo GetMethod<T>(
    Expression<Action<T>> methodSelector)
{
    var body = (MethodCallExpression)methodSelector.Body;
    return body.Method;      
}

[TestMethod]
public void Test1()
{
    var expectedMethod = typeof(Foo)
        .GetMethod("Bar", new Type[] { typeof(Func<>) });

    var actualMethod = 
        GetMethod<Foo>(foo => foo.Bar<object>((Func<object>)null)
        .GetGenericMethodDefinition();

    Assert.AreEqual(expectedMethod, actualMethod);
}


Surprisingly, it looks like you'll need to call GetMethods() and loop over the methods until you find the one you want.

For example:

var yourMethod = typeof(Foo).GetMethods()
    .First(m => m.Name == "Bar" 
             && m.GetParameters().Length == 1
             && m.GetParameters()[0].ParameterType.ContainsGenericParameters
             && m.GetParameters()[0].ParameterType.GetGenericTypeDefinition() == typeof(Func<>));


I don't think you can do this directly using GetMethod. I suspect you'll have to iterate over all the methods called Bar, then:

  • Check that the method has one type parameter
  • Check that the method has one normal parameter
  • Use the type parameter to make a Func<T> (with typeof(Func<>).MakeGenericType) and check that the parameter type matches that.

LINQ is good for this sort of thing. Complete sample:

using System;
using System.Reflection;
using System.Linq;

public class Foo
{
    public void Bar<T>(Func<T> f) { }
    public void Bar<T>(Action<T> a) { }
}

class Test
{
    static void Main()
    {
        var methods = from method in typeof(Foo).GetMethods()
                      where method.Name == "Bar"
                      let typeArgs = method.GetGenericArguments()
                      where typeArgs.Length == 1
                      let parameters = method.GetParameters()
                      where parameters.Length == 1
                      where parameters[0].ParameterType == 
                            typeof(Func<>).MakeGenericType(typeArgs[0])
                      select method;

        Console.WriteLine("Matching methods...");
        foreach (var method in methods)
        {
            Console.WriteLine(method);
        }
    }
}

Basically generics and reflection are really nasty in combination, I'm afraid :(


You need to specify a concrete type using MethodInfo.MakeGenericMethod.

However, I should point out, that getting the right type to invoke MakeGenericMethod on is not easy when you have an overloaded generic method.

Here is an example:

var method = typeof(Foo)
                 .GetMethods()
                 .Where(x => x.Name == "Bar")
                 .Where(x => x.IsGenericMethod)
                 .Where(x => x.GetGenericArguments().Length == 1)
                 .Where(x => x.GetParameters().Length == 1)
                 .Where(x => 
                     x.GetParameters()[0].ParameterType == 
                     typeof(Action<>).MakeGenericType(x.GetGenericArguments()[0])
                 )
                 .Single();

 method = method.MakeGenericMethod(new Type[] { typeof(int) });

 Foo foo = new Foo();
 method.Invoke(foo, new Func<int>[] { () => return 42; });


You'll struggle with just with just GetMethod - you could try something along the lines of;

var method = (from m in typeof(Foo).GetMethods()
  where
    m.IsGenericMethodDefinition == true &&
    m.Name == "Bar" &&
    m.GetParameters().Length > 0 &&
    m.GetParameters()[0].ParameterType.GetGenericTypeDefinition() == parameterType
  select m).FirstOrDefault();
0

精彩评论

暂无评论...
验证码 换一张
取 消