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

Получить имя метода, используя выражение

Я знаю, что на этом сайте есть несколько ответов, и я извиняюсь, если это в какой-то мере повторяется, но все те, что я нашел, не делают то, что я пытаюсь сделать.

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

Скажем, я хочу получить имя метода в этом интерфейсе:

public interface IMyInteface
{
    void DoSomething(string param1, string param2);
}

В настоящее время я могу получить имя с помощью ЭТОГО метода:

 MemberInfo GetMethodInfo<T>(Expression<Action<T>> expression)
 {
        return ((MethodCallExpression)expression.Body).Method;
 }

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

var methodInfo = GetMethodInfo<IMyInteface>(x => x.DoSomething(null, null));
Console.WriteLine(methodInfo.Name);

Но я ищу версию, в которой я могу получить имя метода без указания параметров (null, null)

вот так:

var methodInfo = GetMethodInfo<IMyInteface>(x => x.DoSomething);

Но все попытки не скомпилируются

Есть ли способ сделать это?

4b9b3361

Ответ 1

x => x.DoSomething

Чтобы сделать это компилируемым, я вижу только два способа:

  • Перейдите в общий путь и укажите его как Action<string, string>
  • Укажите Action<string, string> в качестве целевого типа делегирования самостоятельно: GetMethodInfo<IMyInteface>(x => new Action<string,string>(x.DoSomething))

если вы согласны пойти со вторым, что позволяет пропустить аргументы, вы можете написать свой метод GetMethodInfo следующим образом:

    MemberInfo GetMethodInfo<T>(Expression<Func<T, Delegate>> expression)
    {
        var unaryExpression = (UnaryExpression) expression.Body;
        var methodCallExpression = (MethodCallExpression) unaryExpression.Operand;
        var methodInfoExpression = (ConstantExpression) methodCallExpression.Arguments.Last();
        var methodInfo = (MemberInfo) methodInfoExpression.Value;
        return methodInfo;
    }

Он работает для вашего интерфейса, но, вероятно, потребуется некоторое обобщение, чтобы это работало с любым способом, который вам подходит.

Ответ 2

Следующее совместимо с .NET 4.5:

public static string MethodName(LambdaExpression expression)
{
    var unaryExpression = (UnaryExpression)expression.Body;
    var methodCallExpression = (MethodCallExpression)unaryExpression.Operand;
    var methodCallObject = (ConstantExpression)methodCallExpression.Object;
    var methodInfo = (MethodInfo)methodCallObject.Value;

    return methodInfo.Name;
}

Вы можете использовать его с выражениями типа x => x.DoSomething, но для этого потребуется некоторая упаковка в общие методы для разных типов методов.

Вот обратная версия:

private static bool IsNET45 = Type.GetType("System.Reflection.ReflectionContext", false) != null;

public static string MethodName(LambdaExpression expression)
{
    var unaryExpression = (UnaryExpression)expression.Body;
    var methodCallExpression = (MethodCallExpression)unaryExpression.Operand;
    if (IsNET45)
    {
        var methodCallObject = (ConstantExpression)methodCallExpression.Object;
        var methodInfo = (MethodInfo)methodCallObject.Value;
        return methodInfo.Name;
    }
    else
    {
        var methodInfoExpression = (ConstantExpression)methodCallExpression.Arguments.Last();
        var methodInfo = (MemberInfo)methodInfoExpression.Value;
        return methodInfo.Name;
    }
}

Проверьте этот пример кода на Ideone. Обратите внимание, что Ideone не имеет .NET 4.5.

Ответ 3

Проблема заключается в том, что x.DoSomething представляет группу методов. И вы должны как-то явно указать, какой тип делегата вы хотите преобразовать в эту группу методов, чтобы можно было выбрать правильный член группы. И не имеет значения, содержит ли эта группа только один член.

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

Сноуборд-ответ содержит полезные советы о возможных решениях.

Ответ 4

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

MemberInfo info = GetActionInfo<IMyInterface, string, string>(x => x.DoSomething);

или, для методов с возвращаемым значением

MemberInfo info = GetFuncInfo<IMyInterface, object, string, string>(x => x.DoSomethingWithReturn);  

где

object DoSomethingWithReturn(string param1, string param2);

Как и в случае с Framework, Action < > и Func < > делегирует до 16 параметров, вы должны иметь методы GetActionInfo и GetFuncInfo, которые принимают до 16 параметров (или более, хотя я думаю, что рефакторинг является разумным, если у вас есть методы с 16 параметрами). Гораздо больше кода, но улучшение синтаксиса.

Ответ 5

Если ваше приложение разрешит зависимость от Moq (или аналогичную библиотеку), вы можете сделать что-то вроде этого:

class Program
{
    static void Main(string[] args)
    {
        var methodName = GetMethodName<IMyInteface>(x => new Action<string,string>(x.DoSomething));
        Console.WriteLine(methodName);
    }

    static string GetMethodName<T>(Func<T, Delegate> func) where T : class
    {
        // http://code.google.com/p/moq/
        var moq = new Mock<T>();
        var del = func.Invoke(moq.Object);
        return del.Method.Name;
    }
}

public interface IMyInteface
{
    void DoSomething(string param1, string param2);
}

Ответ 6

Если вы nameof() с использованием оператора nameof() вы можете использовать следующий подход.

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

// As extension method
public static string GetMethodName<T>(this T instance, Func<T, string> nameofMethod) where T : class
{
    return nameofMethod(instance);
}

// As static method
public static string GetMethodName<T>(Func<T, string> nameofMethod) where T : class
{
    return nameofMethod(default);
}

Использование:

public class Car
{
    public void Drive() { }
}

var car = new Car();

string methodName1 = car.GetMethodName(c => nameof(c.Drive));

var nullCar = new Car();

string methodName2 = nullCar.GetMethodName(c => nameof(c.Drive));

string methodName3 = GetMethodName<Car>(c => nameof(c.Drive));