You need to pass an Expression
instead of an Action
. It's actually not that hard to use expression trees for this once you understand what the tree looks like.
The line of code:
(MyClass c) => c.TestMethod();
Can be broken down as as a lambda expression (the entire block), containing one parameter (c
, on the left side), and a body (c.TestMethod()
, on the right side).
Then the "body" is a method call on a specific object (which is the parameter, c
), an actual method (TestMethod
), and a set of arguments (in this case, there aren't any).
Visually:
LambdaExpression [ (MyClass c) => c.TestMethod() ]
/ \
/ \
/ \
Parameters Body [ MethodCallExpression: c.TestMethod() ]
| / \
| / \
1: MyClass c Object [c] \
/\
/ \
/ \
Method [TestMethod] Arguments [Empty]
What you want is the method name, inside the method call expression, inside the body of the lambda expression. So the code to get this is:
static string GetInnerMethodName<T>(Expression<Action<T>> expr)
{
MethodCallExpression mce = expr.Body as MethodCallExpression;
return (mce != null) ? mce.Method.Name : null;
}
Of course, this will only work if the Expression<Action<T>>
passed in is a genuine method call expression; the consumer of this method could technically pass in any expression, in which case this will just return null
. You can adjust this to throw an exception instead, return a default value, or perform whatever other action you think is appropriate.
You don't need to do anything special to use this - it's the same usage as your original method:
string methodName = GetInnerMethodName<MyClass>(c => c.TestMethod());