2

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

2
  • Perhaps it's because they're structs (value types), but they should be auto-boxed... Commented Jan 27, 2011 at 5:47
  • 6
    @Charlie: Why should they be boxed automatically? If you want to make an expression tree "by hand" then you are responsible for making the type algebra work out according to the rules of the system; it's not going to fix your mistakes for you. Commented Jan 27, 2011 at 7:25

2 Answers 2

2

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).

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks. I actually looked for a Box method on Expression but didn't realize that TypeAs performs that. Is there a good document going into that kind of detail on Expression trees?
1

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

1 Comment

Thanks. You also found the solution.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.