Get the Name of a Method Using an Expression

Get the name of a method using an expression

x => x.DoSomething

In order to make this compilable I see only two ways:

  1. Go non-generic way and specify it's parameter as Action<string, string>
  2. Specify Action<string, string> as your target delegate type by yourself: GetMethodInfo<IMyInteface>(x => new Action<string,string>(x.DoSomething))

if you are ok to go with second one, which allows you to omit arguments then you can write your GetMethodInfo method as follows:

    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;
}

It works for your interface, but probably some generalization will be required to make this working with any method, that's up to you.

Getting method name from Expression

The main problem is that in your call, you are not actually calling the method, but returning it. This caused the expression tree to contain a call to a method called CreateDelegate.
Once we have that in our methodCallExpression we need to extract the object, and read its value, which is your method.

Disclaimer: This worked for me with a scenario that I think was equal to yours. If it, however, is the best way to solve the larger problem I do not know. It seems to be fairly slow.

var unaryExpression = (UnaryExpression)expression.Body;
var methodCallExpression = (MethodCallExpression)unaryExpression.Operand;
var constantExpression = (ContantExpression)methodCallExpression.Object;
var methodInfo = (MethodInfo)constantExpression.Value;
return methodInfo.Name

Another option would be to provide an actual method call to the method, like so:

GetActionName<EventsController, IEnumerable<EventDto>>(
c => c.GetEventsByIdLocation(0));

Which would require you to change the method to:

string GetActionName<TController, TResult>(
Expression<Func<TController, TResult>> expression)
{
return ((MethodCallExpression)expression.Body).Method.Name;
}

This solution will probably perform a lot faster. Allthough I havn't done any benchmarks or anything. Also, this option does not tie you to only being able to get the names of methods that take one int parameter.

How To Get Name of Calling Method and Executing Method of Lambda Expression

The expression given in the lambda being a delegate, you will have to traverse into its content to get the actual method details. I suggest updating the parameter type for the Helper method (MeasureExecution) to be Expression<Func<T>> instead of Func<T> as an Expression type would allow you to easily examine the contents of a Func. I did try a sample with the below snippet and it worked as per the expectation. I used a static variable in place of your LogDetails to make my life easier in double checking what is being sent.

public static T MeasureExecution<T>(Expression<Func<T>> func, MethodBase sourceMethod)
{
T funcResult;

funcResult = func.Compile()();

try
{
var executingMethod = string.Empty;
var methodExpression = func.Body as MethodCallExpression;

if (methodExpression != null)
{
executingMethod = methodExpression.Method != null ? methodExpression.Method.Name : "Cannot find method details";
}

MethodInformation = string.Format("Method Being Executed: {0}, Executing Source Class: {1}, Executing Source Method: {2}", executingMethod, sourceMethod.ReflectedType.Name, sourceMethod.Name);
}
catch { }

return funcResult;
}

public static string MethodInformation { get; private set; }

And this is the output I get -
Method Being Executed: GetData, Executing Source Class: DataBinder, Executing Source Method: FindData

Selecting a class method name using Lambda Expression

This can be achieved with a little bit of digging into the LambdaExpression like this:

string GetMethodCallName(LambdaExpression expression)
{
var unary = (UnaryExpression)expression.Body;
var methodCall = (MethodCallExpression)unary.Operand;
var constant = (ConstantExpression)methodCall.Object;
var memberInfo = (MemberInfo)constant.Value;

return memberInfo.Name;
}

Unfortunately GetMethodCallName cannot be called directly with the x => x.GetRoles syntax you want since there is no type argument to define what x is. This means you'll need to create an overload of AddMethod for every number of input arguments you want to support for both Action and Func:

void AddMethod<T>(Expression<Func<T, Action>> expression);
void AddMethod<T, Arg1>(Expression<Func<T, Action<Arg1>>> expression);
void AddMethod<T, Arg1, Arg2>(Expression<Func<T, Action<Arg1, Arg2>>> expression);

void AddMethod<T, TResult>(Expression<Func<T, Func<TResult>>> expression);
void AddMethod<T, Arg1, TResult>(Expression<Func<T, Func<Arg1, TResult>>> expression);
void AddMethod<T, Arg1, Arg2, TResult>(Expression<Func<T, Func<Arg1, Arg2, TResult>>> expression);

Then in each AddMethod implementation call GetMethodCallName with the expression argument:

void AddMethod<T, Arg1>(Expression<Func<T, Action<Arg1>>> expression)
{
var methodName = GetMethodCallName(expression);
}

Then you can call AddMethod and specify the containing type and the return and input types of the method:

.AddMethod<IRoleService>(x => x.GetRoles);
.AddMethod<IRoleService, TResult>(x => x.GetRoles);
.AddMethod<IRoleService, Arg1, TResult>(x => x.GetRoles);

Where:

  • TResult is the return type of .GetRoles
  • Arg1 is the type of the first argument of .GetRoles

how to get parameter names from an expression tree?

Expression<Action<Thing>> exp = o => o.Method(1, 2, 3);
var methodInfo = ((MethodCallExpression)exp.Body).Method;
var names = methodInfo.GetParameters().Select(pi => pi.Name);

extracting method name from linq expression

A bit of digging with the debugger and I've answered my own question:

public static class MethodUtils
{
public static string NameFromExpression(LambdaExpression expression)
{
MethodCallExpression callExpression =
expression.Body as MethodCallExpression;

if(callExpression == null)
{
throw new Exception("expression must be a MethodCallExpression");
}

return callExpression.Method.Name;
}
}

Any comments on this implementation?

Get Method Name Using Lambda Expression

There are two ways to do this:

1: You could make overloads that take the various Func and Action delegates(eg Expression<Func<T, Func<TParam1,TParam2, TReturn>>. Note that your callers would need to specify the generic parameters explicitly, either in the method call or by creating the delegate. This would be used like this:

ForResource<Order>().Performing(o => new Action<string>(o.AddItem)).AllowUsersHaving(new Claim());

2: You could take an Expression<Action> that contains a method call, and parse out the MethodInfo being called from the expression tree. This would be used like this:

ForResource<Order>().Performing(o => { o.AddItem(null); }).AllowUsersHaving(new Claim());


Related Topics



Leave a reply



Submit