Search code examples
c#dynamicexpression-treesexpandoobject

How can I apply an implicit cast on numeric types using Expression Trees?


I have an ExpandoObject with an int field, and I want to cast it to a decimal using an expression tree.

Here is the method I'm using :

private static Expression<Func<dynamic, decimal>> CreateLambdaCastExpression()
    {
        // source
        var sourceParameterExpression = Expression.Parameter(typeof (object), "source");

        var binder = Binder.GetMember(
            CSharpBinderFlags.None, "IntProp", typeof (ExpressionTreeUtils),
            new[] {CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)});
        // source.sourceProperty
        var sourcePropertyExpression = Expression.Dynamic(
            binder, typeof (object), sourceParameterExpression);

        // (decimal) source;
        var castedValueExpression = Expression.Convert(sourcePropertyExpression, typeof (decimal));

        // () =>  (decimal) source;
        return Expression.Lambda<Func<dynamic, decimal>>(castedValueExpression,
            sourceParameterExpression);
    }

Calling it this way causes an InvalidCastException:

        dynamic source = new ExpandoObject();
        source.IntProp = 1;

        decimal r = CreateLambdaCastExpression().Compile()(source);

If I set source.IntProp to 1m, it works (obviously)

I've read on msdn that ExpressionConvert only performs implicit conversion on user-defined types, so that might be the explanation.

Any idea on how to perform implicit casts on numeric types?


Solution

  • Change these lines:

    // CSharpBinderFlags.ConvertExplicit: explicit cast 
    // (will convert double to decimal)
    // CSharpBinderFlags.None: implicit cast
    // (will convert int to decimal, won't convert double to decimal)
    var convert = Binder.Convert(CSharpBinderFlags.ConvertExplicit, typeof(decimal), typeof(ExpressionTreeUtils));
    
    // (decimal) source;
    var castedValueExpression = Expression.Dynamic(
        convert, typeof(decimal), sourcePropertyExpression);
    

    Note the comment about implicit/explicit casting.

    (so the only thing that changes is how the var castedValueExpression is built)

    ideone with full example.