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

前端 未结 5 836
误落风尘
误落风尘 2021-01-02 09:25

I have this type that contains two overloads of a generic method. I like to retrieve one of the overloads (with the Func parameter) using reflection. T

相关标签:
5条回答
  • 2021-01-02 09:34

    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 :(

    0 讨论(0)
  • 2021-01-02 09:38

    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<>));
    
    0 讨论(0)
  • 2021-01-02 09:47

    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 讨论(0)
  • 2021-01-02 09:49

    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);
    }
    
    0 讨论(0)
  • 2021-01-02 09:49

    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; });
    
    0 讨论(0)
提交回复
热议问题