4

I've decided to take some golden time to learn a bit more about Expressions. I'm trying a very simple exercise, namely adding two numbers. I've hit an exception that's proving to be tricky to search for.

Here's my code

Expression<Func<int,int,int>> addExpr = (x, y) => x + y;
var p1 = Expression.Parameter(typeof(int), "p1");
var p2 = Expression.Parameter(typeof(int), "p2");
var lambda = Expression.Lambda<Func<int,int,int>>(addExpr, p1, p2); //<-here
var del = lambda.Compile();
var result = del(2,3); //expect 5

but this is throwing an ArgumentException : Expression of type 'System.Func`3[System.Int32,System.Int32,System.Int32]' cannot be used for return type 'System.Int32'

at the line indicated above. What have I done wrong?

spender
  • 117,338
  • 33
  • 229
  • 351
  • What are you trying to do in this code? Are you trying to build (x,y) => x + y lambda using expressions only? – petro.sidlovskyy Sep 05 '12 at 12:35
  • Just poking around. Ultimate goal is an exercise to write a Countdown numbers game solver. http://en.wikipedia.org/wiki/Countdown_%28game_show%29#Numbers_round Seems to me that expression trees would be a good means of tackling this, but it's all very new and shiny to me ATM. – spender Sep 05 '12 at 12:46

3 Answers3

4

You need to wrap the addExpr in an invoke using the expression parameters

Expression<Func<int,int,int>> addExpr = (x, y) => x + y;
var p1 = Expression.Parameter(typeof(int), "p1");
var p2 = Expression.Parameter(typeof(int), "p2");
var invokeExpression=Expression.Invoke(addExpr,p1,p2);
var lambda = Expression.Lambda<Func<int,int,int>>(invokeExpression,p1,p2);
var del = lambda.Compile();
var result=del(2,3);

The invoke is how you type p1 to x and p2 to y, alternatively you could just write the above as

var p1 = Expression.Parameter(typeof(int), "p1");
var p2 = Expression.Parameter(typeof(int), "p2");
var lambda=Expresion.Lambda<Func<int,int,int>>(Expression.Add(p1,p2),p1,p2);
var del = lambda.Compile();
var result=del(2,3);

Otherwise you need to grab the expression body into the lambda and pass the expression parameters.

var lambda=Expresion.Lambda<Func<int,int,int>>(addExpr.Body,addExpr.Parameters);
Bob Vale
  • 18,094
  • 1
  • 42
  • 49
2

Your code should be:

var lambda = Expression.Lambda<Func<Expression<Func<int, int, int>>, int, int>(addExpr, p1, p2);

Your current code expects an int and your passing in Expression<Func<int, int, int>>.

Update

Actually the above won't compile, you would need to do:

var lambda = Expression.Lambda<Func<int, int, int>>(Expression.Add(p1, p2), p1, p2);
James
  • 80,725
  • 18
  • 167
  • 237
  • Yeah, that is not going to work, unless you compile and call the passed expression. – leppie Sep 05 '12 at 12:33
  • @leppie yeah I just tested the code there, I have updated my answer to use `Expression.Add` instead. – James Sep 05 '12 at 12:37
1

You need to decompose addExpr's body or preferably just write it from scratch ie Expression.Add(p1,p2) instead of addExpr.

leppie
  • 115,091
  • 17
  • 196
  • 297