29

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?

svick
  • 236,525
  • 50
  • 385
  • 514
Andre
  • 3,717
  • 4
  • 25
  • 29
  • Do not have VS at the moment, but I think you should try accepting `Delegate` or `Action` in non-generic alternative. In this case you should be able to pass a method group – Snowbear Nov 22 '11 at 10:39
  • If I change the input to Delegate it wont compile and the error is cannot convert method group to delegate. If i change it to Action it seems to work but the expression is converted to a UnaryExpression and the method is null and I cant see where to get this from the UnaryExpression – Andre Nov 22 '11 at 11:01
  • affter converting.. have you tried `var methodInfo = GetMethodInfo(DoSomething);` instead of `var methodInfo = GetMethodInfo(x => x.DoSomething);`? Also, I still think specifying (default(string), default(string)) - is more readable, and you can support overloading methods in this case – Alexander Mavrinsky Nov 22 '11 at 11:22
  • I cant use GetMethodInfo(DoSomething) by specifying do something directly i need to have a reference to the instance, but there are no instance, thats why it must be an expression, IMyInterface is never actually called with the concrete instance but only to extract the metdatata. I dont know if i am making sense – Andre Nov 22 '11 at 11:50
  • very close: [get-method-name-using-lambda-expression](http://stackoverflow.com/questions/2314329/get-method-name-using-lambda-expression) – nawfal Dec 13 '13 at 11:31
  • The returned `MethodInfo` from your approach is not very correct everytime, see this thread [lambda-expression-not-returning-expected-memberinfo](http://stackoverflow.com/questions/6658669/lambda-expression-not-returning-expected-memberinfo) – nawfal Dec 21 '13 at 06:12
  • Update for C# 6.0: use the new `nameof` operator. Super easy and nets you compile-time checking in case of changes. Example: `Console.WriteLine(nameof(DoSomething));` See: [link](https://msdn.microsoft.com/en-us/library/Dn986596.aspx) – soslo Aug 13 '15 at 14:02

6 Answers6

17
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.

Snowbear
  • 16,924
  • 3
  • 43
  • 67
  • I was hoping for something a little less verbose, but nevertheless, this works just fine, thanks. – Andre Nov 22 '11 at 14:45
  • 6
    I am curious, and I know it has been like 2 years already so I hope you can still answer: Is there a way to allow syntax like this: `GetMethodInfo(x => x.DoSomething);`? – 9ee1 May 11 '13 at 07:09
  • 3
    A note on this approach, this works only in .NET 3.5 and 4.0, not in 4.5. In the latter case, the method call expression is represented differently, and hence parameter order differs. In that case you have to do: `var methodInfoExpression = (ConstantExpression)methodCallExpression.Object;`. Overall I find this approach too cumbersome. – nawfal Dec 13 '13 at 07:26
  • @nawfal, expression trees are generated by the compiler. It doesn't matter which .Net version you target. You probably mean that the C#2012 compiler generates a different expression tree than earlier versions. – adrianm Dec 13 '13 at 07:37
  • @adrianm no not at all. I did mean that the same C# 5 compiler on .NET 3.5 and 4.5 behaves differently. In fact the frameworks do. The implementation of the nuances is in the library. The compiler just emits the final part, which is not what this difference is about. I was asking you to be careful even about the framework differences. – nawfal Dec 13 '13 at 08:20
  • Also this fails in derived : base scenarios like this: http://stackoverflow.com/questions/6658669/lambda-expression-not-returning-expected-memberinfo. Not nitpicking, just awareness.. – nawfal Dec 13 '13 at 09:36
  • 1
    @nawfal The C# 5.0 compiler does behave differently depending on the target framework. If I'm looking at it correctly, for .Net 4.0, it generates `Call` to `Delegate.CreateDelegate`, while for .Net 4.5, it's the new `MethodInfo.CreateDelegate`. – svick Dec 13 '13 at 14:29
  • @svick spot on. Whoa, I didnt know 4.5 had `CreateDelegate` on `MethodInfo` itself, thanks! :) Btw, a hybrid approach is not very difficult to write.. – nawfal Dec 13 '13 at 16:48
  • Update for C# 6.0: use the new `nameof` operator. Super easy and nets you compile-time checking in case of changes. Example: `Console.WriteLine(nameof(DoSomething));` See: [link](https://msdn.microsoft.com/en-us/library/Dn986596.aspx) – soslo Aug 13 '15 at 14:02
13

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.

BartoszKP
  • 34,786
  • 15
  • 102
  • 130
Kędrzu
  • 2,385
  • 13
  • 22
4

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.

svick
  • 236,525
  • 50
  • 385
  • 514
2

This is a new answer to an old question, but responds to the "verbose" complaint of the accepted answer. It requires more code, but the result is a syntax like:

MemberInfo info = GetActionInfo<IMyInterface, string, string>(x => x.DoSomething);

or, for methods with a return value

MemberInfo info = GetFuncInfo<IMyInterface, object, string, string>(x => x.DoSomethingWithReturn);  

where

object DoSomethingWithReturn(string param1, string param2);

Just like the framework provides Action<> and Func<> delegates up to 16 parameters, you have to have GetActionInfo and GetFuncInfo methods that accept up to 16 parameters (or more, although I'd think refactoring is wise if you have methods with 16 parameters). A lot more code, but an improvement in the syntax.

Joe Enzminger
  • 11,110
  • 3
  • 50
  • 75
  • 1
    I am hoping less verbose means I can do something like this: `GetMethodInfo(x => x.DoSomething);`? I am a little confused how to implement your suggestion though. Can you provide a quick code sample? – 9ee1 May 11 '13 at 07:11
  • The only way to do it (that I know of) without passing the parameters is to include the parameter types in the generic specification - i.e GetMethodInfo(x => x.DoSomething). – Joe Enzminger May 22 '13 at 22:00
2

If you are ok with using the nameof() operator you can use the following approach.

One of the benefits is not having to unwrap an expression tree or supply default values or worry about having a non-null instance of the type with the method.

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

Usage:

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));
seangwright
  • 17,245
  • 6
  • 42
  • 54
1

If your application would allow a dependency on Moq (or a similar library), you could do something like this:

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);
}
Wallace Kelly
  • 15,565
  • 8
  • 50
  • 71
  • 1
    -1, this give the handle to the intermediate anonymous delegate you're creating (to pass to the `GetMethodInfo` method), not of that of the actual member which you're trying to represent (`DoSomething`). – nawfal Dec 13 '13 at 06:40
  • @nawful The original poster asked for the string Name, not the original MethodInfo. I have updated my example to clarify. – Wallace Kelly Dec 13 '13 at 17:32
  • I worry if it doesnt give the correct result still. Have you tested this? – nawfal Dec 13 '13 at 17:34
  • Yes, I have tested this. – Wallace Kelly Dec 13 '13 at 23:28
  • Oh well, I read it wrongly, your edit was unwarranted then :). In one way then this is a better solution than the accepted one, so +1. But the downside is that you require a `T` instance to work your way. Not very sure which is more performant. Thanks for adding! – nawfal Dec 14 '13 at 03:56
  • This is quite an overkill: creating a Mock class (expensive!), depending on third party library, relying on real lamda functions instead of expression trees, also overcomplicated usage... – Kędrzu Dec 17 '16 at 00:25