开发者

How do you access delegate target method argument(s)?

开发者 https://www.devze.com 2023-02-13 22:01 出处:网络
Within the following method, I would like to access any optional arguments that are contained within the Action:

Within the following method, I would like to access any optional arguments that are contained within the Action:

public static class Validator开发者_如何学PythonEngine
{
    public static void Validate(Action someMethodWithOptionalArguments)
    {
        object target = someMethodWithOptionalArguments.Target;
    }
}

So if I called this method like so:

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

I would like to be able to access the model argument passed into the Action. I'm not even interested in invoking this Action.

I'm thinking that there is something that can be done with the Target property of the Action because I can see the model when debugging. I just can't figure it out programmatically.


If you want to inspect it but not execute it, this is a prime example for an Expression. Just change the signature from Action to Expression<Action>. This will give you an expression tree that you can analyse. For a basic example:

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();
    }
}

To support a richer set of nodes and scenarios, see this richer version.


If Marc Gravell's detailed answer does not meet your needs, the only way I can think of accomplishing what you desire is via reflection. State that is captured in a lambda closure become public fields (conveniently named the same as the captured variables) of a compiler-generated class. This class defines a method containing the contents of the lambda itself and will be the method for the Action delegate. An instance of this class will be the delegate's target.

For simplicity's sake, I'll use C# 4.0's dynamic keyword for this example. For your needs, you'd probably want to use "proper" reflection to discover the fields so you don't have to know their names in advance (plus the fact that some of the fields would be optional).

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

//...

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

The result is that "ick" is written to the console. This is how I feel about this solution. Besides the feeling this just oozes hack, the fact the type we're using here has [CompilerGenerated] slapped on it should give you pause: there is no contract between you and the compiler. Later versions of the compiler are free to change how this all works behind the scenes. Ultimately, this solution is fragile at best.

0

精彩评论

暂无评论...
验证码 换一张
取 消