views:

430

answers:

1

I am missing the obvious: How do I access the value of a parameter inside a lambda expression expression tree?

Scenario: For a delegate x I dynamically create a lambda expression with an expression tree body which has the same signature as the delegate x. Inside the lamdba's body, I do some validation, checking, logging stuff (this is just testing code, not production), and then I invoke the original delegate x with the original parameters. If the delegate has a return value, this gets returned as well.

That works quite well (including passing the parameters to the original delegate).

But I am hitting a brick wall if I want to access the original parameter values passed to the delegate/lambda.

pseudo code:

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  A: 

Looks like you confused the expression trees compiler too much (well, I was confused by this code too). I can see what you tried to do: you got an element from an array, then you decided to loop over the array. But you couldn't do array[ParameterExpression], so you used ArrayIndex. But...

But ArrayIndex in fact doesn't return "string". It returns MethodCallExpression. So, in this "Assign" expression you actually have ParameterExpression and MethodCallExpression. The ET compiler is smart enough to compile these expressions and try to assign results. But the result of your MethodCallExpression is ParameterExpression. When you had paramDefs[0], you had ParameterExpression right away and compiler could handle that. But compiling nested expressions is harder and it is totally unclear whether you really want to compile this nested expression or not.

What you can do is to compile and invoke the MethodCallExpression yourself, so you will have ParameterExpression in the Assign expression (as you had before). It may look like this:

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

But it might be very heavy in terms of performance (plus the code is ugly). So, I'd stick with your idea of pulling the loop out of the expression tree.

Alexandra Rusina