-->

Using a strongly typed method as an argument witho

2020-06-18 09:46发布

问题:

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

回答1:

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.



回答2:

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.



回答3:

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