(Action <t>)。Name不返回预期值</t>

时间:2010-04-05 23:45:06

标签: c# generics delegates

我有以下方法(用于在单元测试中生成友好的错误消息):

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"。我应该如何更改代码以获得预期的结果?

2 个答案:

答案 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