Получение значения времени выполнения ParameterExpression в дереве выражений - PullRequest
5 голосов
/ 09 марта 2010

Мне не хватает очевидного: как мне получить доступ к значению параметра внутри дерева лямбда-выражений?

Сценарий: для делегата x я динамически создаю лямбда-выражение с телом дерева выражений, которое имеет ту же сигнатуру, что и делегат x. Внутри тела lamdba я делаю некоторые проверки, проверки, ведение журнала (это просто тестирование кода, а не производства), а затем вызываю исходный делегат x с исходными параметрами. Если у делегата есть возвращаемое значение, это также возвращается.

Это работает довольно хорошо (включая передачу параметров исходному делегату).

Но я бью кирпичную стену, если хочу получить доступ к исходным значениям параметров, переданным делегату / лямбде.

псевдокод:

var del = new Func<string, int>(_=> {return 42;});
var paramDefs = Array.ConvertAll<ParameterInfo, ParameterExpression>(del.Method.GetParameters(), _ => { return Expression.Parameter(_.ParameterType, _.Name); });
var variableTest = Expression.Variable(typeof(string), "str");

var expression = Expression.Block(
  new [] { variableTest },
  // this line assigns the actual run time value (which is what I need) of the parameter to the variable - but I cannot hardcode the index.
  //Expression.Assign(variableTest, paramDefs[0]) 
  // this line would assigns the ParameterExpression object (causing a run-time exception since the type of the variable is string) ... I need the _value_ of the first (or nth) parameter.
  Expression.Assign(variableTest, Expression.ArrayIndex(Expression.Constant(paramDefs), Expression.Constant(0)))
);
var lamdba = Expression.Lambda(del.GetType(), expression, "foo", paramDefs);
var del2 = lamdba.Compile() as Func<string, int>;
del2("this is a test");

1 Ответ

3 голосов
/ 11 марта 2010

Похоже, вы слишком запутали компилятор деревьев выражений (ну, этот код меня тоже смутил). Я вижу, что вы пытались сделать: вы получили элемент из массива, а затем решили перебрать массив. Но вы не могли сделать массив [ParameterExpression], поэтому вы использовали ArrayIndex. Но ...

Но ArrayIndex фактически не возвращает «строку». Возвращает MethodCallExpression. Итак, в этом выражении «Назначить» у вас есть ParameterExpression и MethodCallExpression. Компилятор ET достаточно умен, чтобы скомпилировать эти выражения и попытаться присвоить результаты. Но результатом вашего MethodCallExpression является ParameterExpression. Когда у вас был paramDefs [0], у вас был ParameterExpression сразу, и компилятор мог справиться с этим. Но компилировать вложенные выражения сложнее, и совершенно неясно, действительно ли вы хотите скомпилировать это вложенное выражение или нет.

То, что вы можете сделать, - это самостоятельно скомпилировать и вызвать выражение MethodCallExpression, чтобы у вас было выражение ParameterExpression в выражении Assign (как и раньше). Это может выглядеть так:

// Replace Assign in your Block expression.
Expression.Assign(variableTest, Expression.Lambda<Func<ParameterExpression>>(Expression.ArrayIndex(Expression.Constant(paramDefs), Expression.Constant(0))).Compile()()),

Но это может быть очень тяжело с точки зрения производительности (плюс код уродлив). Итак, я бы придерживался вашей идеи вытащить цикл из дерева выражений.

...