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