Подтвердить что ты не робот

Get methodinfo из ссылки на метод С#

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

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

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

4b9b3361

Ответ 1

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

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

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

Первоначальный ответ был на этот вопрос; fooobar.com/questions/125849/...

Ответ 2

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

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.

Используйте MemberExpression и MemberInfo вместо этого, если вы хотите поддерживать свойства getters и seters.

Ответ 3

Класс тестирования

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

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

Ответ 4

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

typeof(Program).GetMethods();

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

Вы можете повторить его, если хотите, например, указать имена всех ваших методов 10.000+.

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

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

Ответ 5

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

var callback = typeof(BlogController).GetMethod(nameof(BlogController.GetBlogs));

Ответ 6

Я создал шаблон 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 установлено значение Нет
  • вы можете создать более или менее функции, установив максимальную переменную в соответствующую настройку.

Ответ 7

Позвольте мне добавить некоторые объяснения к проблеме здесь. Мы ищем метод GetMethodInfo(SomeMethodSymbol) который возвращает информацию о данном методе. Это не просто, потому что методы могут быть перегружены в С#. Таким образом, в основном вам нужно добавить дополнительные вызовы в вызов, чтобы компилятор (и другие анализаторы кода, такие как 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, который не принимает аргументов, таких как 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.