Получить имя метода, используя выражение
Я знаю, что на этом сайте есть несколько ответов, и я извиняюсь, если это в какой-то мере повторяется, но все те, что я нашел, не делают то, что я пытаюсь сделать.
Я пытаюсь указать информацию о методе, чтобы я мог получить имя в безопасном виде, не используя строки.
Поэтому я пытаюсь извлечь его с выражением.
Скажем, я хочу получить имя метода в этом интерфейсе:
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);
Но все попытки не скомпилируются
Есть ли способ сделать это?
Ответы
Ответ 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));