Get the name of a method using an expression
x => x.DoSomething
In order to make this compilable I see only two ways:
- Go non-generic way and specify it's parameter as
Action<string, string>
- 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
Performance Cost of 'Try' in C#
Simple Way to Display Row Numbers on Wpf Datagrid
Disable Transitive Project Reference in .Net Standard 2
Detect Windows Font Size (100%, 125%, and 150%)
Apiresource VS APIscope VS Identityresource
How to Drag and Drop a Button from One Panel to Another Panel
Disabling Screen Saver and Power Options in C#
Open Link in New Tab (Webbrowser Control)
How to Check If Thread Finished Execution
MVC 4 How Pass Data Correctly from Controller to View
C# Automatic Property Deserialization of JSON
How to Intercept Console Output
How Can a Windows Service Determine Its Servicename
Adding Elements to an Xml File in C#
C# 4.0: How to Use a Timespan as an Optional Parameter with a Default Value