Получение MethodInfo из правильной перегрузки универсального метода - PullRequest
9 голосов
/ 14 декабря 2010

У меня есть этот тип, который содержит две перегрузки универсального метода. Мне нравится получать одну из перегрузок (с параметром Func<T>), используя отражение. Проблема, однако, заключается в том, что я не могу найти правильный тип параметра для предоставления метода Type.GetMethod(string, Type[]).

Вот мое определение класса:

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

И вот что я придумал, к сожалению, безуспешно:

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

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

    Assert.IsNotNull(method); // Fails
}

Как я могу получить MethodInfo универсального метода, параметры которого мне известны?

Ответы [ 5 ]

9 голосов
/ 06 января 2011

Почему вы не используете деревья выражений?Это делает это намного проще:

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);
}
4 голосов
/ 14 декабря 2010

Удивительно, но похоже, что вам нужно будет вызвать GetMethods() и перебрать методы, пока не найдете нужный.

Например:

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<>));
1 голос
/ 14 декабря 2010

Я не думаю вы можете сделать это напрямую, используя GetMethod. Я подозреваю, что вам придется перебирать все методы, называемые Bar, затем:

  • Убедитесь, что метод имеет один параметр типа
  • Убедитесь, что метод имеет один нормальный параметр
  • Используйте параметр типа, чтобы создать Func<T>typeof(Func<>).MakeGenericType) и убедитесь, что тип параметра соответствует этому.

LINQ хорош для такого рода вещей. Полный образец:

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);
        }
    }
}

По сути, я боюсь, что дженерики и рефлексы действительно противны в комбинации: (

1 голос
/ 14 декабря 2010

Вам необходимо указать конкретный тип, используя MethodInfo.MakeGenericMethod.

Однако я должен отметить, что получить правильный тип для вызова MakeGenericMethod непросто, если у вас перегруженный универсальный метод.

Вот пример:

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 голосов
/ 14 декабря 2010

Вы будете бороться только с GetMethod - вы можете попробовать что-то вроде;

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();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...