Search code examples
c#linq-expressions

C# Linq Expression tree invalid cast exception going from int to double


I am trying to use C# object initializer syntax with LINQ Expressions trees and I used type conversions (i.e. Expression.Convert) so I shouldn't get invalid cast exceptions for simple type conversions (i.e. int to double) but I still get invalid cast exceptions.

Error:

Unhandled exception. System.InvalidCastException: Unable to cast object of type 'System.Int32' to type 'System.Double'.
   at lambda_method1(Closure, Dictionary`2)
   at Program.Main(String[] args) in 

Lambda expression I am generating dynamically:

Dictionary<string, object> $var0) => new Foo() {
    Name = (string)$var0["Name"],
    Salary = (double)$var0["Salary"]
}

Code:

internal class Program
{
    public static void Main(string[] args)
    {
        var foo = (Foo)InstantiateAndInitializeType(typeof(Foo))(new Dictionary<string, object>
        {
            ["Name"] = "foobar",
            ["Age"] = 12,
            ["Salary"] = 20
        });
        
        Console.WriteLine(JsonConvert.SerializeObject(foo, Formatting.Indented));
    }

    public static Func<Dictionary<string, object>, object> InstantiateAndInitializeType(Type type)
    {
        var dictArg = Expression.Parameter(typeof(Dictionary<string, object>));
        
        var body = Expression.MemberInit(Expression.New(typeof(Foo)), typeof(Foo)
            .GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.GetProperty | BindingFlags.SetProperty)
            .Where(x => x.GetCustomAttribute<RequiredMemberAttribute>() != null)
            .Select(x => Expression.Bind(x, Expression.Convert(Expression.Property(dictArg, "Item", Expression.Constant(x.Name)), x.PropertyType))));

        var lambdaExpr = Expression.Lambda<Func<Dictionary<string, object>, object>>(body, dictArg);
        var func = lambdaExpr.Compile();
        return func;
    }
}

class Foo
{
    public required string Name { get; set; }
    
    
    public double Age { get; set; }
    
    
    public required double Salary { get; set; }
}

Solution

  • Since you don't know the "real" type of the object in each Dictionary entry, you must use Convert.ChangeType to convert to the desired target type, then use a cast to unbox the object return when you want a value type.

    public static Func<Dictionary<string, object>, object> InstantiateAndInitializeType(Type type) {
        // Convert.ChangeType
        var ctMI = typeof(Convert).GetMethod("ChangeType", new[] { typeof(Object), typeof(Type) });
        // could put this in a static outside the method since it won't change at runtime
    
        // (Dictionary<string,object> p)
        var dictParm = Expression.Parameter(typeof(Dictionary<string, object>), "p");
    
        // new Foo()
        var newFooExpr = Expression.New(typeof(Foo));
    
        // { {prop} = ({prop type})Convert.ChangeType(p[{prop name}], {prop type}), ... }
        var propBindExprs = typeof(Foo)
            .GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.GetProperty | BindingFlags.SetProperty)
            .Where(propInfo => propInfo.GetCustomAttribute<RequiredMemberAttribute>() != null)
            .Select(propInfo =>
                // {prop} = ({prop type})Convert.ChangeType(p[{prop name}], typeof({prop type}))
                Expression.Bind(
                    propInfo, // {prop}
                    // ({prop type})
                    Expression.Convert(
                        // Convert.ChangeType(p[{prop name}], typeof({prop type}))
                        Expression.Call(null,
                                        ctMI, // Convert.ChangeType
                                        // p[{prop name}]
                                        Expression.Property(dictParm, "Item", Expression.Constant(propInfo.Name)),
                                        // typeof({prop type})
                                        Expression.Constant(propInfo.PropertyType)),
                        // {prop type}
                        propInfo.PropertyType)));
    
        // new Foo() { {prop} = ({prop type})Convert.ChangeType(p[{prop name}], {prop type}), ... }
        var body = Expression.MemberInit(Expression.New(typeof(Foo)), propBindExprs);
        // (Dictionary<string,object> p) => new Foo() { {prop} = ({prop type})Convert.ChangeType(p[{prop name}], {prop type}), ... }
        var lambdaExpr = Expression.Lambda<Func<Dictionary<string, object>, object>>(body, dictParm);
    
        var func = lambdaExpr.Compile();
        return func;
    }
    

    This isn't terribly efficient if you are building and compiling a lot of init functions, but if you re-use a standard one that expects a given Dictionary, it might be worthwhile to add some optimizations to remove the call to Convert.ChangeType for when the actual type in the Dictionary can be cast to the property type or matches the property type exactly (e.g. String).