获取在操作中执行的方法的自定义属性

时间:2016-02-12 12:13:21

标签: c# lambda action

这是我的示例metod

[TestStep("Do something")]
private void DoSomething()
{   
}

每个看起来与上面一样的方法都是以需要记录方法参数的方式执行的:

private void LogStep(Action action)
{
    string stepName = "[" + action.Method.Name + "] ";
    var descr = Attribute.GetCustomAttribute(action.Method, typeof(TestStepAttribute)) as TestStepAttribute;

    if (descr == null)
    {
        this.TestLog.AddWarningMessage(
            (action.Method.DeclaringType == null ? string.Empty : action.Method.DeclaringType.FullName + ".") + action.Method.Name
            + ": missing description");

        return;
    }

    stepName += descr.Description;

    this.TestLog.EndGroup();

    this.TestLog.BeginGroup(stepName);
}

在这里,我遇到了一个问题。像

一样执行LogStep
LogStep(DoSomething)

完美无缺,但是当我使用lambda表达式

执行它时
LogStep(() => DoSomething())

它告诉我TestStepAttribute中没有Action类型的属性。

乍一看似乎与How do I get the custom attributes of a method from Action<T>?类似,但就我而言,我既不能将Action的类型更改为Expression<Action>,也不知道方法名称。

任何建议都会有所帮助。

3 个答案:

答案 0 :(得分:3)

使用lambda表达式执行它时,lambda表达式本身就是方法。碰巧在它的体内有一个方法调用,但是可能还有其他东西(比如new object())。访问此内部方法的属性的唯一方法是将lambda表达式传递为Expression并分析表达式。

为了处理这两种情况,您需要两次LogStep重载。但是,在调用不明确的情况下,您不能将LogStep(Action)LogStep(Expression<Action>)作为重载。但如果其中一个是LogStep(Delegate),它就会起作用。

private void LogStep(Delegate action)
{
    var attr = (TestStepAttribute)Attribute
        .GetCustomAttribute(action.Method, typeof(TestStepAttribute));
    Console.WriteLine("LogStep(Delegate action):  " + attr?.Description);
}

private void LogStep(Expression<Action> actionExpr)
{
    string descr = null;
    var methodCall = actionExpr.Body as MethodCallExpression;
    if (methodCall != null) {
        var attribs = methodCall.Method.GetCustomAttributes(typeof(TestStepAttribute), true);
        if (attribs.Length > 0) {
            descr = ((TestStepAttribute)attribs[0]).Description;
        }
    }
    Console.WriteLine("LogStep(Expression<Action> actionExpr):  " + descr);
}

试验:

LogStep(new Action(DoSomething)); // new Action() Is required here. Calls first overlaod.
LogStep(() => DoSomething()); // Calls second overload.
LogStep(() => new object());  // Calls second overload.

请注意,如果需要执行该方法,可以编译并执行lambda表达式。

答案 1 :(得分:2)

  

完美无缺,但是当我使用lambda表达式

执行它时      

LogStep(()=&gt; DoSomething())它告诉我没有属性   该Action中的TestStepAttribute类型。

当然很难找到任何属性,因为你传递一个lambda表达式,它基本上是一个方法,在那个方法中你传递你的方法DoSomething(),并在lambda表达式上完成检查。

答案 2 :(得分:1)

Lambda表达式只是另一种方法。当你查看action.Method时,这就是你得到的方法(action.Target将包含一个闭包,如果有的话)。

最后,你所拥有的只是:

void SomeAnonymousMethod()
{
  DoSomething();
}

要获得实际调用的方法,您必须首先反编译匿名方法。当然,你可能正在使用lambda语法传递参数,同时仍然使用无标量的动作,它会变得更加疯狂:

class SomeClosure
{
  string argument1;
  int argument2;

  void AnonymousMethod()
  {
    var data = GetSomeData(argument2);

    DoSomething(data, argument1);
  }
}

您如何告诉 DoSomething是您需要元数据的方法?

使用lambda表达式没有办法解决这个问题。幸运的是,无论如何你似乎并不需要这样,因为你从来没有打过这个论点。不要使用Action,只需使用Delegate,您就可以直接传递所需的任何方法:

void DoSomething(string something, string otherThing)
{
  ... // Not important
}

void LogStep(Delegate someDelegate)
{
  ... // Exactly as before
}

LogStep((Action<string, string>)DoSomething);

遗憾的是,你必须手动转换调用,否则编译器会给你一个错误;但是,您可以为LogStep方法本身保留相同的签名。或者,您可以使用简单的T4模板来创建LogStep方法的多个重载,以便您可以避免手写代码中的显式强制转换。