C# construct lambda using Expression.Call doesn't like certain types as params?

c# expression-trees lambda

Question

For various reasons I'm constructing a C# lambda dynamically using the expression tree facilities. e.g. I can make a Func<string,bool> at runtime as shown in the following snippet.

   public static bool myMethod( object obj ) {  … }

    // Construct a Func<string,bool>
    var myMethod = GetType().GetMethod("myMethod");
    var lambdaParams = new ParameterExpression[] {Expression.Parameter(typeof (string))};
    var callMyMethod = Expression.Call(myMethod, lambdaParams);
    var lambda = Expression.Lambda(typeof(Func<string,bool>), callMyMethod, lambdaParams);
    var del = (Func<string,bool>)lambda.Compile();
    del("foo"); // works

However if I use the same code to try to make a Func<int,bool> or a Func<DateTime,bool> it blows up where indicated with the following strange exception:

    // Construct a Func<DateTime,bool> or perhaps a struct type fails... why?
    var myMethod = GetType().GetMethod("myMethod");
    var lambdaParams = new ParameterExpression[] {Expression.Parameter(typeof (DateTime))};
    var callMyMethod = Expression.Call(myMethod, lambdaParams);  // Blows up here…

System.ArgumentException: Expression of type 'System.DateTime' cannot be used for parameter of type 'System.Object' of method 'Boolean myMethod(System.Object)'

So, string works and List<string> works but int32 does not work nor does DateTime. What is going on? I don't know how the deep internals of C# work but I am guessing it's due to int really being handled as a primitive and maybe DateTime (being a struct) as well...

Any help with this would be greatly appreciated.

thanks, Pat

Accepted Answer

As I understand it, Expression.Call doesn't perform auto-boxing of value-type arguments. I'm unable to find any documentation to that effect, but it is mentioned on this forum page.

One workaround would be to explicitly do the boxing conversion in the expression with Expression.TypeAs.

Creates a UnaryExpression that represents an explicit reference or boxing conversion where null is supplied if the conversion fails.

In your case, this should work:

var boxedParams = lambdaParams.Select(p => Expression.TypeAs(p, typeof(object)))
                              .ToArray();

var callMyMethod = Expression.Call(myMethod, boxedParams);

(You don't need the fancy lambdas if there's only one parameter)

Depending on the real usage, you may have to check if the boxing conversion is necessary depending on whether the type(s) in question is(are) value-type(s).


Popular Answer

Check this out: you have to box the DateTime, since DateTime isnt' a reference type!

// Construct a Func<DateTime,bool>
var myMethod = typeof(Program).GetMethod("myMethod");
var param = Expression.Parameter(typeof(DateTime));
var boxy = Expression.TypeAs(param, typeof(object));

var callMyMethod = Expression.Call(myMethod, boxy);
var lambda = Expression.Lambda(typeof(Func<DateTime, bool>), callMyMethod, new ParameterExpression[] { param });
var del = (Func<DateTime,bool>)lambda.Compile();
del(DateTime.Now); // works



Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Is this KB legal? Yes, learn why
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Is this KB legal? Yes, learn why