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; }
}
Since you don't know the "real" type of the
objectin eachDictionaryentry, you must useConvert.ChangeTypeto convert to the desired target type, then use a cast to unbox theobjectreturn when you want a value type.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 toConvert.ChangeTypefor when the actual type in theDictionarycan be cast to the property type or matches the property type exactly (e.g.String).