5

Is it possible to pass in the strongly typed name of a method as a lambda expression without also providing the parameters and/or parentheses?

For example, if I have the following method:

public class CalleeClass
{
    public void MethodA(obj param1, obj param2)
    {
        ...
    }
}

I would like to call this method elsewhere via:

return new MyClass<CalleeClass>(c => c.MethodA); //Note: no ()'s or arguments

Where MyClass would be responsible for, say, MVC routing using the method name as the target. The goal here is that we want to be able to use strongly typed views via controller methods, and I don't want to have to provide "dumb" parameters that don't get used.

Presently, I am using code similar to the following in order to use the method names, but this style still requires passing in fake arguments and/or parentheses.

public void MyClass<T>(Expression<Action<T>> action)
{
    var methodName = (action.Body as MethodCallExpression).Method.Name;
}

EDIT: Sorry for the confusion, but I initially tried to simplify the issue by only including what I thought you'd need, and in doing so left out some key info. The ultimate goal here is to have MyClass receive a generic type + lambda expression, and the lambda expression can pass in the strongly typed method name without instantiating an object. -MB

Matt Beckman
  • 5,022
  • 4
  • 29
  • 42
  • Can you not set optional parameters? Also: MethodCallExpression won't work unless it has the parenthesis I'm pretty sure. – chemicalNova Nov 08 '11 at 00:24
  • The parameters aren't dumb; they are used by the compiler... – Marc Gravell Nov 08 '11 at 00:41
  • No offense to the parameters, but we only need the method name for routing, and it seems strange to give the impression that we're instantiating an object. I'm not convinced this is even possible, which is why I came here. :) – Matt Beckman Nov 08 '11 at 00:49
  • Why not to use MethodInfo? Something like: var method = typeof(AClass).GetMethods().Where((m) => m.Name.Equals(@"MethodA")).FirstOrDefault(); And then use method as the parameter you need in MyClass. – Diego Nov 08 '11 at 13:33

3 Answers3

2

You can actually pass the method without parameters:

class PassActionTest
{
    public void Test( )
    {
        var c = new C();
        var myClass =  new MyClass(c.MethodA); 
    }
}

class MyClass
{
    public MyClass(Action<object,object> action)
    {
        string methodName = action.Method.Name;
    }
}

class C
{
    public void MethodA(object param1, object param2)
    {
    }
}

EDIT: According to Matt Beckman's EDIT, the class containing MethodA should not be instantiated. My new solution is:

class PassActionTest
{
    public void Test()
    {
        var myClass = new MyClass(c => c.MethodA);
    }
}

class MyClass
{
    public MyClass(Expression<Func<C, Action<object, object>>> expr)
    {
        UnaryExpression unaryExpr = (UnaryExpression)expr.Body;
        MethodCallExpression methodCallExpr = (MethodCallExpression)unaryExpr.Operand;
        ConstantExpression constantExpr = (ConstantExpression)methodCallExpr.Arguments[2];
        MethodInfo methodInfo = (MethodInfo)constantExpr.Value;
        string methodName = methodInfo.Name;
    }

}

class C
{
    public void MethodA(object param1, object param2)
    {
    }
}

It is a bit complicated to analyse the expression, but I have tested it and it works.

Olivier Jacot-Descombes
  • 104,806
  • 13
  • 138
  • 188
  • In trying to simplify the problem on here, I think I forgot to add that this line should be: _return new MyClass(c => c.MethodA)_, as I don't want to instantiate the class that has the MethodA method. See changes above. – Matt Beckman Nov 08 '11 at 18:32
  • 1
    See [this comment](http://stackoverflow.com/questions/8225302/get-the-name-of-a-method-using-an-expression#comment30751822_8228144) and [this answer](http://stackoverflow.com/a/26976055/1270174) for usage on v4.5 runtime. – Bill Boga Mar 25 '15 at 17:51
  • Starting with C#6.0 you will be able to use the `nameof()` operator. `string s = nameof(c.MethodA)` – Olivier Jacot-Descombes Mar 26 '15 at 21:27
1

If you're just needing the method name, I'd recommend using Delegate (http://msdn.microsoft.com/en-us/library/system.delegate.aspx):

public MyClass(Delegate action)
{
    var methodName = action.Method.Name;
}

This works except I think you'll need to specify the delegate type when passing it in:

{
    ...
    return new MyClass((Action<object,object>)c.MethodA);
}

This'll keep it all strongly-typed such that refactoring will work, too.

Matt DeKrey
  • 11,582
  • 5
  • 54
  • 69
0

You can declare a delegate matching the signature of the method MethodA() and use the delegate as the parameter to the constructor of MyClass class

public delegate void MethodADelegate(object param1, object param2);

public MyClass(MethodADelegate methodParam)
{
    //use the methodParam parameter
}

Alternatively, you can use the Action<> delegate present in .Net as suggested by @Olivier

S2S2
  • 8,322
  • 5
  • 37
  • 65