По сути, вам нужно объявить параметр как Func
, который соответствует сигнатуре метода, которую вы хотите принять, а затем обернуть его в Expression
, чтобы компилятор выдал вам дерево выражений, а не фактическоеделегировать.Затем вы можете пройтись по дереву выражений, чтобы найти MethodCallExpression
, из которого вы можете получить имя метода.(Кстати, пример кода в предоставленной вами ссылке будет работать с вызовами методов, так же, как вы хотите, в дополнение к свойствам)
какая подпись должна быть DoSomethingWithMethod
Это зависит от сигнатуры метода, который вы хотели бы принять в качестве параметра.Если какой-то метод выглядит следующим образом:
public MyReturnType SomeMethod(MyParameterType parameter) {}
, тогда подпись DoSomethingWithMethod
будет выглядеть следующим образом:
public void DoSomethingWithMethod(Expression<Func<MyParameterType,MyReturnType>> methodExpression) {}
Вы также можете сделать его общим, если вы хотите принимать методы с немного отличающимися сигнатурами.(однако, если вы хотите принимать методы с различным числом параметров, вам придется использовать перегрузку, также компилятор C #, вероятно, не будет автоматически разрешать параметры универсального типа в этой ситуации, и вам придется указывать их явно)
public void DoSomethingWithMethod<TParam,TReturn>(Expression<Func<TParam,TReturn>> methodExpression) {}
и что он на самом деле должен делать
Я предполагаю, что этот вопрос на самом деле, как мне получить имя метода в виде строки из дерева выражений?
Есть несколько различных способов сделать это, и это зависит от того, насколько надежным вы хотите, чтобы ваш код был.Учтите, что указанная выше сигнатура метода позволяет передавать делегат, который значительно сложнее, чем просто один вызов метода.Например:
DoSomethingWithMethod(t => t.SomeMethod().SomeOtherMethod(5) + AnotherThing(t));
Если вы выполните поиск по дереву выражений, сгенерированному из приведенного выше, вы найдете довольно много вызовов методов, а не один.Если вы просто хотите убедиться, что переданный параметр является одиночным вызовом метода, вероятно, проще просто попытаться привести свойство Body
выражения к MethodCallExpression
public void DoSomethingWithMethod<TParam,TReturn>(Expression<Func<TParam,TReturn>> methodExpression)
{
if (methodExpression.Body is MethodCallExpression methodCall)
{
var methodName = methodCall.Method.Name;
//...
}
}
Другой вариант - использоватьшаблон посетителя, это особенно полезно, если у вас более сложный сценарий, например, если вы хотите получить список всех имен методов, например, если их несколько, или поддерживает смесь вызовов свойств или методов и т. д.
Для этого параметра создайте класс, который наследует ExpressionVisitor
, переопределяет соответствующие методы в базовом классе и где-то сохраняет результаты.Вот пример:
class MyVisitor : ExpressionVisitor
{
public List<string> Names { get; } = new List<string>();
protected override Expression VisitMember(MemberExpression node)
{
if(node.Member.MemberType == MemberTypes.Method)
{
Names.Add(node.Member.Name);
}
return base.VisitMember(node);
}
}
вы можете назвать его так:
var visitor = new MyVisitor();
visitor.Visit(methodExpression.Body);
var methodName = visitor.Names[0];
//...
Наконец, чтобы вызвать его, вы не сможете использовать сокращенный режим «группа методов»вызова DoSomethingWithMethod
, поскольку компилятор C # не способен автоматически преобразовывать группу методов в дерево выражений (однако он может автоматически преобразовывать ее в обычный делегат, к которому вы привыкли).
вы не можете сделать:
DoSomethingWithMethod(SomeMethod);
вместо этого оно должно выглядеть как лямбда-выражение:
DoSomethingWithMethod(t => SomeMethod(t));
или, если нет параметров:
DoSomethingWithMethod(() => SomeMethod());