开发者

How to get runtime-argument info from extression tree parameter array

开发者 https://www.devze.com 2023-01-05 23:52 出处:网络
Okay first to explain the rules: I need a function that constructs a delegate matching any delegate type that encapsulates a body of which invokes a delegate of type (Object) (Object[] args) with \'a

Okay first to explain the rules:

I need a function that constructs a delegate matching any delegate type that encapsulates a body of which invokes a delegate of type (Object) (Object[] args) with 'args' containing all of the arguments passed to the original delegate during invocation.

My work so far:

    delegate void TestDelegate(int x, int y);
    delegate object TestDelegate2(object[] args);

    static void Main(string[] sargs)
    {
        TestDelegate d = (TestDelegate)CreateAnonymousFromType(typeof(TestDelegate));
        object ret = d.DynamicInvoke(2, 6);

        if (ret != null) { Console.WriteLine(ret); }

        Console.ReadKey();
    }

    static void SpecialInvoke(int x, int y) 
    {
        Console.WriteLine("x: {0}\r\ny: {1}", x, y);
    }

    static Delegate CreateAnonymousFromType(Type type)
    {
        MethodInfo method = type.GetMethod("Invoke");

        TestDelegate2 _delegate = new TestDelegate2(
            delegate(object[] args) 
            {
                Console.WriteLine("x: {0}\r\ny: {1}", args[0], args[1]);
                return "This is the return";
            });


        var typeargs = CreateArgs(method.GetParameters());
        var argindex = -1;

        var tmp = Expression.Parameter(typeof(Object), "tmp");
        var index = Expression.Parameter(typeof(int), "index");

        var length = Expression.Constant(typeargs.Length);

        var _break = Expression.Label("breakto");

        var delegateargs = Expression.Parameter(typeof(object[]), "args");

        return Expression.Lambda(
            type,
            Expression.Block(
                new[] { tmp, index, delegateargs },
                Expression.Assign(index, Expression.Constant(0)),
                Expression.Assign(delegateargs, Expression.NewArrayBounds(typeof(Object), length)),
                Expression.Loop(
                    Expression.IfThenElse(Expression.LessThan(index, length),
                        Expression.Block(
                            Expression.Assign(tmp, Expression.Convert(typeargs[++argindex], typeof(Object))),
                            Expression.Assign(Expression.ArrayAccess(delegateargs, index), tmp),
                            Expression.PostIncrementAssign(index)
                        ),
                        Expression.Break(_break)
                    ),
                    _break
                ),
                Expression.Call(_delegate.Method, delegateargs)
            ),
            typeargs
        ).Compile();
    }

    static ParameterExpression[] CreateArgs(ParameterInfo[] _params)
    {
        ParameterExpression[] ret = new ParameterExpression[_params.Length];

        for (int i = 0; i < ret.Length; i++)
            ret[i] = Expression.Parameter(_params[i].ParameterType, _params[i].Name);

        return ret;
    }

Now this SORTA works... I only get the value of typeargs[0] passed to the delegate "TestDelegate2" for both parameters x and y, the 'args' parameter at runtime is object[] { 2, 2 } and I can't for the life of me find a way to increment "argindex" inside the scope of the argument iteration... parame开发者_StackOverflow中文版ter count at compile time is indefinate. Anybody know how I can solve this?

I've tried just copying the argument array using Expression.NewArrayInit(typeof(Object), typeargs) but that fails saying it can't use Int32 to initialize an array of Object

I've also tried this: var arguments = Expression.Constant(typeargs);

And accessing the value of "arguments" at "index", however this produces the strings "x" and "y" .. apparently the names of the arguments and not their values.

This is honestly my first major attempt at using expression trees so any help.. no matter how little. Would be appreciated.

Thank you.


I think you were on the right track with Expression.NewArrayInit. You can fix the "An expression of type 'System.Int32' cannot be used to initialize an array of type 'System.Object'" error by using Expression.Convert to insert a conversion for each parameter:

var typeargs = CreateArgs(method.GetParameters());
return Expression.Lambda(
    type,
    Expression.Call(_delegate.Method, Expression.NewArrayInit(typeof(object),
        typeargs.Select(arg => Expression.Convert(arg, typeof(object)))
        )),
    typeargs
).Compile();
0

精彩评论

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