получить methodinfo из ссылки на метод C # - PullRequest
17 голосов
/ 21 февраля 2012

Мы можем использовать ключевое слово C # typeof, когда мы хотим получить экземпляр Type для указанного типа. Но что я могу использовать, если я хочу получить MethodInfo метода по его ссылке?

Например, у меня есть простое консольное приложение. Содержит Program.Main метод. Я хочу получить MethodInfo, используя что-то вроде methodinfoof(Program.Main). У меня есть эта проблема, потому что имена методов могут измениться, поэтому я не могу просто использовать Type.GetMethodInfo(string MethodName) для этого.

У меня есть около 10 000 методов, для которых я хотел бы получить MethodInfo, поэтому добавление любых пользовательских атрибутов или чего-либо еще в мои методы не является решением.

Ответы [ 7 ]

15 голосов
/ 21 февраля 2012

Небольшая адаптация ранее опубликованного ответа, но, похоже, этот пост в блоге достигает того, о чем вы просили;http://blog.functionalfun.net/2009/10/getting-methodinfo-of-generic-method.html

Пример использования будет следующим:

var methodInfo = SymbolExtensions.GetMethodInfo(() => Program.Main());

Первоначальный ответ был на этот вопрос;https://stackoverflow.com/a/9132588/5827

12 голосов
/ 21 февраля 2012

Вы можете использовать деревья выражений для нестатических методов.Вот пример.

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

public static class MethodInfoHelper
{
    public static MethodInfo GetMethodInfo<T>(Expression<Action<T>> expression)
    {
        var member = expression.Body as MethodCallExpression;

        if (member != null)
            return member.Method;

        throw new ArgumentException("Expression is not a method", "expression");
    }
}

Вы бы использовали его так:

        MethodInfo mi = MethodInfoHelper.GetMethodInfo<Program>(x => x.Test());
        Console.WriteLine(mi.Name);

Test () - функция-член, объявленная в классе Program.

UseMemberExpression и MemberInfo вместо этого, если вы хотите поддерживать методы получения и установки свойств.

6 голосов
/ 21 февраля 2012

Тестовый класс

public class  Foo
{
    public void DoFoo()
    {
        Trace.WriteLine("DoFoo");
    }

    public static void DoStaticFoo()
    {
        Trace.WriteLine("DoStaticFoo");
    }
}

И вы можете сделать что-то вроде этого

MethodInfo GetMethodInfo(Action a)
{
    return a.Method;
}

var foo = new Foo();
MethodInfo mi = GetMethodInfo(foo.DoFoo);
MethodInfo miStatic = GetMethodInfo(Foo.DoStaticFoo);

//do whatever you need with method info

Обновить
За комментарий @Greg, если у вас есть какие-то параметры дляметоды, которые вы можете использовать Action<T>, Action<T1, T2>, Action<T1, T2, T3> или Func<T1>, неудобство заключается в том, что вам все равно придется записывать перегрузки для GetMethodInfo.

5 голосов
/ 06 февраля 2017

Я знаю, что это очень старый пост, но я просто добавлю его для того, кто все еще ищет простое решение этого вопроса. Просто кажется, что никто не думал о простейшем решении:

typeof(Program).GetMethods();

Возвращает массив с MethodInfo всех методов в классе Program, независимо от атрибутов или от того, имеют параметры или нет.

Вы можете повторить ove, если хотите, например,перечислите имена всех ваших 10.000+ методов.

Вы также можете сделать typeof(Program).GetMethod(nameof(Program.Main)); таким образом, если имя метода изменится, рефакторинг Visual Studio также переименует его и здесь.

ПРИМЕЧАНИЕ: "nameof""ключевое слово не было доступно 5 лет назад, когда был опубликован вопрос.

2 голосов
/ 12 декабря 2017

Возможно, не идеальный способ, но это может помочь:

var callback = typeof(BlogController).GetMethod(nameof(BlogController.GetBlogs));
1 голос
/ 06 июня 2019

Позвольте мне добавить некоторые объяснения к проблеме здесь.Мы ищем метод GetMethodInfo(SomeMethodSymbol), который возвращает информацию о данном методе.Это не просто, потому что методы могут быть перегружены в C #.Таким образом, в основном вам нужно добавить дополнительные вызовы в вызов, чтобы компилятор (и другие анализаторы кода, такие как Intellisense) понимали, о каком методе вы говорите.

Скажем, например, я ищу информацию о методе Math.Abs.Затем я должен указать, какую именно перегруженную версию метода, которую я ищу, точно:

// int
MethodInfo info1 = ((Func<int, int>)Math.Abs).Method;

// or double ?
MethodInfo info2 = ((Func<double, double>)Math.Abs).Method;

Даже если существует только одна существующая перегрузка, как для метода Math.Exp, я все равно должен предоставить сигналы ввода, потому чтометод может быть перегружен в будущем, и код больше не будет компилироваться.

Прямые помощники

С учетом приведенных выше замечаний мы можем предоставить следующий набор вспомогательных методов, чтобы несколько облегчить утомительную задачу приведения каждого метода к своей информации:

public static class GetMethodInfoUtil
{
    // No cast necessary
    public static MethodInfo GetMethodInfo(Action action) => action.Method;
    public static MethodInfo GetMethodInfo<T>(Action<T> action) => action.Method;
    public static MethodInfo GetMethodInfo<T,U>(Action<T,U> action) => action.Method;
    public static MethodInfo GetMethodInfo<TResult>(Func<TResult> fun) => fun.Method;
    public static MethodInfo GetMethodInfo<T, TResult>(Func<T, TResult> fun) => fun.Method;
    public static MethodInfo GetMethodInfo<T, U, TResult>(Func<T, U, TResult> fun) => fun.Method;

    // Cast necessary
    public static MethodInfo GetMethodInfo(Delegate del) => del.Method;
}

Затем вы можете использовать эти помощники следующим образом:

var methodInfos = new[] {

    // Static methods
    GetMethodInfo<int, int>(Math.Abs),
    GetMethodInfo<double, double>(Math.Abs),
    GetMethodInfo<long, long, long>(Math.Max),

    // Static void methods
    GetMethodInfo(Console.Clear),
    GetMethodInfo<string[]>(Main),

    // With explicit cast if too many arguments
    GetMethodInfo((Action<string, object, object>)Console.WriteLine),

    // Instance methods
    GetMethodInfo<string, bool>("".StartsWith),
    GetMethodInfo(new List<int>().Clear),
};

Обратите внимание, что информация о типе все еще должна предоставляться, за исключением метода void static, не принимающего аргументы, такие как Console.Clear.Кроме того, например, методы, фактический экземпляр должен использоваться для получения соответствующего метода, который использует больше ресурсов.

Косвенные помощники

Теперь для некоторых угловых случаев вышеуказанные помощники не будут работать.Скажем, метод использует out параметры, например.В этих особых случаях извлечение информации о методах из лямбда-выражений становится удобным, и мы возвращаемся к решению, предоставленному другими авторами (код вдохновения здесь ):

public static class GetIndirectMethodInfoUtil
{
    // Get MethodInfo from Lambda expressions
    public static MethodInfo GetIndirectMethodInfo(Expression<Action> expression) 
        => GetIndirectMethodInfo((LambdaExpression)expression);
    public static MethodInfo GetIndirectMethodInfo<T>(Expression<Action<T>> expression) 
        => GetIndirectMethodInfo((LambdaExpression)expression);
    public static MethodInfo GetIndirectMethodInfo<T, TResult>(Expression<Func<TResult>> expression) 
        => GetIndirectMethodInfo((LambdaExpression)expression);
    public static MethodInfo GetIndirectMethodInfo<T, TResult>(Expression<Func<T, TResult>> expression) 
        => GetIndirectMethodInfo((LambdaExpression)expression);

    // Used by the above
    private static MethodInfo GetIndirectMethodInfo(LambdaExpression expression)
    {
        if (!(expression.Body is MethodCallExpression methodCall))
        {
            throw new ArgumentException(
                $"Invalid Expression ({expression.Body}). Expression should consist of a method call only.");
        }
        return methodCall.Method;
    }
}

Вы бы использоваливот такие:

int dummyInt;
var moreMethodInfos = new[]
{
    // Extracted from lambdas
    GetIndirectMethodInfo(() => "".StartsWith("")),
    GetIndirectMethodInfo((string s) => s.StartsWith(s)),
    GetIndirectMethodInfo(() => int.TryParse("", out dummyInt)),
};

Обратите внимание, что информация о типе все еще предоставляется косвенно из типа аргумента.Также обратите внимание, что фиктивный аргумент был добавлен только для того, чтобы можно было использовать параметр out.

Полная демонстрационная программа: https://dotnetfiddle.net/CkS075.

1 голос
/ 06 апреля 2014

Я создал шаблон T4, который создает необходимые вспомогательные функции, чтобы помочь вам в этом. Он создает список функций для получения объектов MethodInfo из методов Func <> или Action <>.

Скопируйте следующий код в файл с именем GetMethodInfo.tt:

<#@ template language="C#" #>
<#@ output extension=".cs" encoding="utf-8" #>
<#@ import namespace="System" #>
<#@ import namespace="System.Text" #>
using System;
using System.Linq.Expressions;
using System.Reflection;

namespace Tools
{
    public static class GetMethodInfo
    {
<# int max = 12;
for(int i = 0; i <= max; i++) 
{
    var builder = new StringBuilder();

    for(int j = 0; j <= i; j++) 
    {
        builder.Append("T");
        builder.Append(j);
        if(j != i) 
        {
            builder.Append(", ");
        }
    }

    var T = builder.ToString();
#>
        public static MethodInfo ForFunc<T, <#= T #>>(Expression<Func<T, <#= T #>>> expression)
        {
            var member = expression.Body as MethodCallExpression;

            if (member != null)
                return member.Method;

            throw new ArgumentException("Expression is not a method", "expression");
        }

        public static MethodInfo ForAction<<#= T #>>(Expression<Action<<#= T #>>> expression)
        {
            var member = expression.Body as MethodCallExpression;

            if (member != null)
                return member.Method;

            throw new ArgumentException("Expression is not a method", "expression");
        }

<# } #>
    }
}

Примечания :

  • , пожалуйста, убедитесь, что Действие построения шаблона .tt установлено на Нет
  • Вы можете создавать больше или меньше функций, устанавливая переменную max на соответствующую настройку.
...