views:

8

answers:

1

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... parameter 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.

A: 

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();
Quartermeister
Sweet!! I knew there had to be a solution somewhere... it's easy to find information on making basic expression trees but very little about expressions that take parameters and how to use the parameters in subsequent expression blocks. That worked perfect! And the code is many lines shorter.. kudos to that! =)
SilverX