Search code examples
c#lambdaexpressionexpression-trees

Compiling Expression throws cannot be used for return type ArgumentException


I am trying to get more familiar with expression trees.

I have created a simple switch expression as follows:

var paramExp = Expression.Parameter(typeof(int));
Expression<Func<int>> defaultBodyExp = () => 4;
var switchBodyExp = Expression.Switch(paramExp, defaultBodyExp,
    Expression.SwitchCase(defaultBodyExp, Expression.Constant(1)));
var func = Expression.Lambda<Func<int, int>>(switchBodyExp, paramExp).Compile();
int result = func(6);

But this raises the following exception when trying to call Compile:

Expression of type 'System.Func`1[System.Int32]' cannot be used for return type 'System.Int32'

I can get it working by doing the following:

var paramExp = Expression.Parameter(typeof(int));
//Expression<Func<int>> defaultBodyExp = () => 4;
var defaultBodyMethodInfo = typeof(Program).GetMethod("DefaultBody");
var switchBodyExp = Expression.Switch(paramExp, Expression.Call(defaultBodyMethodInfo),
    Expression.SwitchCase(Expression.Call(defaultBodyMethodInfo), Expression.Constant(1)));
var func = Expression.Lambda<Func<int, int>>(switchBodyExp, paramExp).Compile();
int result = func(6);

public static int DefaultBody()
{
    return 4;
}

But I'm really not sure why my first example doesn't work and I'd love to know how to do that properly so I don't have to declare a method like in my second example. Do I need to be doing some sort of call invoke like in the second example?

I've had a look at some of the other similar answers on SO, but I'm new enough with Expression Trees this that I'm really not sure if they answer my problem or not.


Solution

  • The lambda expression Expression<Func<int>> can only be used directly in a places that expect Func<int> (like Enumerable.Select) or Expression<Func<int>> (like Queryable.Select, but you need to wrap it inside Expression.Quote).

    In order to use it in a places which expect int like in your case, it needs to be converted to an expression that represents the result of the "execution" of the lambda expression. Let change the original code to

    Expression<Func<int>> defaultBodyLambda = () => 4;
    var defaultBodyExp = ???;
    

    and the question is what should ??? be.

    One possible (and the general) solution is to use the Expression.Invoke method:

    var defaultBodyExp = Expression.Invoke(defaultBodyLambda);
    

    Or, since this particular lambda expression has no parameters, we can simply use the LambdaExpression.Body property:

    var defaultBodyExp = defaultBodyLambda.Body;