I know there are a few answers on the site on this and i apologize if this is in any way duplicate, but all of the ones I found does not do what I am trying to do.
I am trying to specify method info so I can get the name in a type safe way by not using strings. So I am trying to extract it with an expression.
Say I want to get the name of a method in this interface:
public interface IMyInteface
{
void DoSomething(string param1, string param2);
}
Currently I can get the name using THIS method:
MemberInfo GetMethodInfo<T>(Expression<Action<T>> expression)
{
return ((MethodCallExpression)expression.Body).Method;
}
I can call the helper method as follows:
var methodInfo = GetMethodInfo<IMyInteface>(x => x.DoSomething(null, null));
Console.WriteLine(methodInfo.Name);
But I am looking for the version that I can get the method name without specifying the parameters (null, null)
like this:
var methodInfo = GetMethodInfo<IMyInteface>(x => x.DoSomething);
But all attempts fail to compile
Is there a way to do this?
A method call expression produces the pure value returned by the method; the type of this value is specified by the return type in the method declaration. But if the method has the return type void, the expression does not produce a value.
A nameof expression is evaluated at compile time and has no effect at run time.
x => x.DoSomething
In order to make this compilable I see only two ways:
Action<string, string>
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.
The following is compatible with .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; }
You can use it with expressions like x => x.DoSomething
, however it would require some wrapping into generic methods for different types of methods.
Here is a backwards-compatible version:
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; } }
Check this sample code on Ideone. Note, that Ideone does not have .NET 4.5.
The problem with this is that x.DoSomething
represents a method group. And you have to somehow explicitly specify what delegate type do you want to convert that method group into, so that the correct member of the group can be selected. And it doesn't matter if that group contains only one member.
The compiler could infer that you mean that one, but it doesn't do that. (I think it's this way so that your code won't break if you add another overload of that method.)
Snowbear's answer contains good advice on possible solutions.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With