【问题标题】:action delegate. How to get methods infos called in delegate?行动代表。如何获取委托中调用的方法信息?
【发布时间】:2012-06-06 22:57:34
【问题描述】:

我需要获取在 Action 委托中调用的方法的 MethodInfo 以检查在 Action 中调用的方法是否具有 MyCustomAttibute

    public void Foo( Action action )
    {
        if(Attribute.GetCustomAttributes(action.Method, typeof(MyCustomAttribute)).Count() == 0)
        {
            throw new ArgumentException("Invalid action");
        }
    }

Foo 方法应该可以如下调用:

    Foo(() =>
    {
            instanceOfFooClass.Method1().Method2();
    });

在 Foo 方法中,我想确保 Method1 和 Method2 具有 MyCustomAttribute。但是 action.Method 给了我 MethodInfo,这是委托的操作,在使用 lambda 表达式时发生。有什么方法可以获取 Method1 和 Method2 MethodInfo 吗?

【问题讨论】:

  • 很好的问题。不是很容易,AFAIK。您可以可能使用Expression<Action> 轻松完成它,但是您不能直接执行它
  • 我同意你必须为此使用表达式树。我不知道这会对性能造成多大影响。
  • 如果不直接,那怎么执行呢?
  • 另外一个问题是带有语句体的lambda表达式不能用作Expression

标签: c# delegates


【解决方案1】:

正如 cmets 中所述,Expression<T> 可能是实现这一目标的最佳方式。但是,它在运行时需要 Compile(),因此应该对其进行性能分析。

使用Expression<T>,您可以轻松访问方法信息,如下所示:

public MethodInfo GetMethodInfo(Expression<Action> action)
{
    return ((MethodCallExpression)action.Body).Method;
}

但是,在执行操作之前,您必须这样做:

private void InvokeMethod(Expression<Action> action)
{
    action.Compile().Invoke();
}

编辑 啊,是的,我忘记了如何访问客户属性。你会这样做:

var methodInfo = ((MethodCallExpression)myAction.Body).Method;
var attributes = methodInfo.GetCustomAttributes<T>(true);

示例 这是一个示例,显示将链式方法调用传递给Expression&lt;Action&gt;

public class ActionTest
{
    public void DoAction(Action action)
    {
        action();
    }

    public void DoExpressionAction(Expression<Action> action)
    {
        var method2Info = ((MethodCallExpression)action.Body).Method;

        // a little recursion needed here
        var method1Info = ((MethodCallExpression)((MethodCallExpression)action.Body).Object).Method;

        var myattributes2 = method2Info.GetCustomAttributes(typeof(MyAttribute), true);
        var myattributes1 = method1Info.GetCustomAttributes(typeof(MyAttribute), true);

        action.Compile().Invoke();
    }
}

[AttributeUsage(AttributeTargets.Method)]
public class MyAttribute : Attribute
{
    private string message;

    public MyAttribute(string message)
    {
        this.message = message;
    }
}

public class MethodTest
{
    [MyAttribute("Number1")]
    public MethodTest Method1()
    {
        Console.WriteLine("Action");
        return this;
    }

    [MyAttribute("Number2")]
    public MethodTest Method2()
    {
        Console.WriteLine("ExpressionAction");
        return this;
    }
}


class Program
{
    static void Main(string[] args)
    {
        ActionTest target = new ActionTest();
        MethodTest instance = new MethodTest();

        target.DoExpressionAction(() => instance.Method1().Method2() );

        Console.ReadLine();
    }

    static void Method1()
    {
        Console.WriteLine("Action");
    }

    static void Method2()
    {
        Console.WriteLine("ExpressionAction");
    }
}

【讨论】:

  • 您能告诉我如何将 () => { instanceOfFooClass.Method1().Method2();} 转换为 Expression 吗?
  • 你不应该改变它。您应该能够直接传递它而无需更改。让我测试一下。
  • 是的,您可以将() =&gt; action 直接传递给Expression&lt;T&gt;。你只需要添加一个使用System.Linq.Expressions
  • 我收到编译错误:“无法将带有语句体的 lambda 表达式转换为表达式树”,同时尝试将 () => 操作作为 Expression 参数传递。我做错了吗?
  • 我刚刚将我的示例添加到答案中。这有帮助吗?
【解决方案2】:

如果您像这样调用Foo() 方法:

Foo(instanceOfFooClass.Method);

您的代码按照您的预期工作(毕竟,无效方法操作)。 顺便说一句,我认为“链接”方法调用实际上很重要,因为您只传递了最后一个。

展示行为的完整示例:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication4
{
    class MyCustomAttribute : Attribute { }
    class FooClass
    {
        [MyCustom]
        public void DecoratedMethod() { Console.WriteLine("Decorated Method - executed."); }
        public void NotDecoratedMethod() { Console.WriteLine("Not Decoreated Method - executed."); }
    }

    class Program
    {
        static void Main(string[] args)
        {
            FooClass instanceOfFooClass = new FooClass();
            Foo(instanceOfFooClass.DecoratedMethod);
            Foo(instanceOfFooClass.NotDecoratedMethod);
            Console.ReadLine();
        }

        public static void Foo(Action action)
        {
            if (Attribute.GetCustomAttributes(action.Method, typeof(MyCustomAttribute)).Count() == 0)
                Console.WriteLine(string.Format("Invalid method {0}", action.Method.Name));
            else
            {
                Console.WriteLine(string.Format("Valid method {0}", action.Method.Name));
                action.Invoke();
            }
        }
    }
}

【讨论】:

  • 我不确定它是否适用于链式方法。在他提供的示例中,您必须以某种方式分析所提供操作的内部调用,以检查方法 1 和方法 2。除非他完全重写他的代码,否则这不适用于简单的委托检查
  • 你当然是对的,这可能不适用于“链”方法......老实说,整个概念对我来说感觉很奇怪。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-23
  • 2011-10-02
相关资源
最近更新 更多