Как получить информацию об аргументах времени выполнения из массива параметров дерева выражений - PullRequest
2 голосов
/ 06 июля 2010

Хорошо, сначала объясню правила:

Мне нужна функция, которая создает делегат, соответствующий любому типу делегата, который инкапсулирует тело, которое вызывает делегат типа (Object) (Object [] args) с 'args ', содержащий все аргументы, переданные исходному делегату во время вызова.

Моя работа на данный момент:

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

Теперь этот SORTA работает ... Я получаю только значение typeargs [0] передан делегату «TestDelegate2» для обоих параметров x и y, параметр «args» во время выполнения - это object [] {2, 2}, и я не могу найти способ увеличить значение «argindex»внутри контекста итерации аргумента ... количество параметров во время компиляции неопределенно.Кто-нибудь знает, как я могу решить эту проблему?

Я пытался просто скопировать массив аргументов, используя Expression.NewArrayInit (typeof (Object), typeargs), но это не удается сказать, что он не может использовать Int32 для инициализации массиваObject

Я также пробовал это: var arguments = Expression.Constant (typeargs);

И получая доступ к значению "arguments" в "index", однако при этом получаются строки "x"и" y ".. очевидно имена аргументов, а не их значения.

Это, честно говоря, моя первая крупная попытка использования деревьев выражений, поэтому любая помощь ... неважно, как мало.Будет оценено.

Спасибо.

1 Ответ

2 голосов
/ 06 июля 2010

Я думаю, что вы были на правильном пути с Expression.NewArrayInit. Можно исправить ошибку «Выражение типа« System.Int32 »нельзя использовать для инициализации массива типа« System.Object »», используя Expression.Convert для вставки преобразования для каждого параметра:

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