如何访问委托目标方法参数?

时间:2011-02-27 07:25:31

标签: c# .net

在以下方法中,我想访问Action中包含的任何可选参数:

public static class ValidatorEngine
{
    public static void Validate(Action someMethodWithOptionalArguments)
    {
        object target = someMethodWithOptionalArguments.Target;
    }
}

所以如果我这样调用这个方法:

ValidatorEngine.Validate(() => UpdateByModel(model));

我希望能够访问传递给Action的模型参数。我甚至都不想调用这个Action。

我认为可以使用Action的Target属性完成某些操作,因为我可以在调试时看到模型。我无法以编程方式解决这个问题。

2 个答案:

答案 0 :(得分:5)

如果你想检查但不执行它,这是Expression的一个主要示例。只需将签名从Action更改为Expression<Action>即可。这将为您提供一个可以分析的表达式树。对于基本示例:

public static class ValidatorEngine
{
    static void Main()
    {
        string model = "abc";
        ValidatorEngine.Validate(() => UpdateByModel(model));
    }
    public static void Validate(Expression<Action> action)
    {
        var methodCall = action.Body as MethodCallExpression;
        if (methodCall == null) throw new InvalidOperationException("Expected a method-call");
        Console.WriteLine("Method: " + methodCall.Method.DeclaringType.Name + "." + methodCall.Method.Name);
        var parameters = methodCall.Method.GetParameters();
        for (int i = 0; i < parameters.Length; i++)
        {
          Console.WriteLine(parameters[i].Name + ": " + Evaluate(methodCall.Arguments[i]));
        }
    }

    static object Evaluate(Expression exp)
    {
        switch (exp.NodeType)
        {
            case ExpressionType.Constant:
                return ((ConstantExpression)exp).Value;
            case ExpressionType.MemberAccess:
                var me = (MemberExpression)exp;
                switch (me.Member.MemberType)
                {
                    case System.Reflection.MemberTypes.Field:
                        return ((FieldInfo)me.Member).GetValue(Evaluate(me.Expression));
                    case MemberTypes.Property:
                        return ((PropertyInfo)me.Member).GetValue(Evaluate(me.Expression), null);
                    default:
                        throw new NotSupportedException(me.Member.MemberType.ToString());
                }
            default:
                throw new NotSupportedException(exp.NodeType.ToString());

        }

    }
    static void UpdateByModel(object model) {
        throw new NotImplementedException();
    }
}

要支持更丰富的节点和方案,请参阅this richer version

答案 1 :(得分:3)

如果Marc Gravell的详细答案不能满足您的需求,我能想到实现您所需要的唯一方法就是通过反思。在lambda闭包中捕获的状态变为编译器生成的类的公共字段(方便地与捕获的变量命名相同)。该类定义了一个包含lambda本身内容的方法,并将成为Action委托的方法。该类的一个实例将是委托的目标。

为简单起见,我将在此示例中使用C#4.0的动态关键字。根据您的需要,您可能希望使用“正确”反射来发现字段,这样您就不必事先知道它们的名称(加上某些字段是可选的)。

void Foo(Action action)
{
    dynamic o = action.Target;
    o.data = "ick";
    action();
}

//...

string data = "ugh";
Foo(() => Console.WriteLine(data));

结果是“ick”被写入控制台。这就是我对这个解决方案的看法。除了感觉这只是渗透黑客,我们在这里使用的类型[CompilerGenerated]打了它的事实应该让你暂停:你和编译器之间没有契约。更高版本的编译器可以随意更改这一切在幕后的工作原理。最终,这种解决方案充其量是脆弱的。