我有以下方法(用于在单元测试中生成友好的错误消息):
protected string MethodName<TTestedType>(Action<TTestedType> call)
{
return string.Format("{0}.{1}", typeof(TTestedType).FullName, call.Method.Name);
}
但是当我按照以下方式调用它时,我没有得到预期的结果:
var nm = MethodName<MyController>(ctrl => ctrl.Create());
运行此代码后,nm
包含"<Create_CreateShowsView>b__8"
,而不是(按预期)"Create"
。我应该如何更改代码以获得预期的结果?
答案 0 :(得分:7)
您需要传递Expression
而不是Action
。一旦你理解了树的样子,实际上并没有那么难以使用表达式树。
代码行:
(MyClass c) => c.TestMethod();
可以像lambda表达式(整个块)一样细分,左侧包含一个参数(c
),右侧包含一个正文(c.TestMethod()
)。
然后“body”是对特定对象(即参数c
)的方法调用,实际方法(TestMethod
)和一组参数(在本例中,没有任何)。
目视:
LambdaExpression [ (MyClass c) => c.TestMethod() ]
/ \
/ \
/ \
Parameters Body [ MethodCallExpression: c.TestMethod() ]
| / \
| / \
1: MyClass c Object [c] \
/\
/ \
/ \
Method [TestMethod] Arguments [Empty]
你想要的是方法名称,在方法调用表达式中,在lambda表达式的主体内。所以得到这个的代码是:
static string GetInnerMethodName<T>(Expression<Action<T>> expr)
{
MethodCallExpression mce = expr.Body as MethodCallExpression;
return (mce != null) ? mce.Method.Name : null;
}
当然,这只有在传入的Expression<Action<T>>
是真正的方法调用表达式时才有效;此方法的使用者可以在技术上传入任何表达式,在这种情况下,这将只返回null
。您可以调整此值以引发异常,返回默认值,或执行您认为合适的任何其他操作。
您不需要做任何特殊的事情来使用它 - 它与原始方法的用法相同:
string methodName = GetInnerMethodName<MyClass>(c => c.TestMethod());
答案 1 :(得分:0)
b__8
事件是由C#编译器为lambda生成的方法的名称。例如,您可以使用Reflector来查看此内容。
如果您需要说“创建”,则必须实际创建名为Create
的方法。当然,它需要适合Action
,因此必须返回void
。
在简单的情况下,您可以创建一个方法,该方法接受一个指示要调用的方法的字符串,并返回一个在指定类型的对象上调用该方法的操作。例如,请参阅this tutorial。