使用C# LINQ表达式树时int转double出现无效强制转换异常
LINQ表达式树中int转double抛出无效强制转换异常
问题现象
在C#中使用LINQ表达式树构建对象初始化器时,已通过Expression.Convert处理类型转换,但int转double这类简单转换仍抛出InvalidCastException。
错误信息
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表达式
Dictionary<string, object> $var0) => new Foo() { Name = (string)$var0["Name"], Salary = (double)$var0["Salary"] }
原始代码示例
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; } }
问题原因
问题出在装箱值类型的转换逻辑:从Dictionary<string, object>取出的20是装箱后的int(类型为object),直接用Expression.Convert尝试将object转成double时,CLR会尝试直接拆箱成double,但装箱的int只能直接拆箱成int,无法直接拆箱为其他值类型,因此抛出异常。
解决方案
需要先将装箱的对象拆箱为其实际类型,再转换为目标类型,或者使用Convert.ChangeType来处理类型转换(该方法会自动处理拆箱和转换逻辑)。
修改InstantiateAndInitializeType方法中的转换逻辑,替换原来的Expression.Convert为调用Convert.ChangeType的表达式:
public static Func<Dictionary<string, object>, object> InstantiateAndInitializeType(Type type) { var dictArg = Expression.Parameter(typeof(Dictionary<string, object>)); // 获取Convert.ChangeType方法 var changeTypeMethod = typeof(Convert).GetMethod("ChangeType", new[] { typeof(object), typeof(Type) }) ?? throw new MissingMethodException("未找到Convert.ChangeType方法"); var bindings = typeof(Foo) .GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.GetProperty | BindingFlags.SetProperty) .Where(x => x.GetCustomAttribute<RequiredMemberAttribute>() != null) .Select(x => { var valueExpr = Expression.Property(dictArg, "Item", Expression.Constant(x.Name)); // 调用Convert.ChangeType转换类型,再转为目标类型 var convertedExpr = Expression.Convert( Expression.Call(changeTypeMethod, valueExpr, Expression.Constant(x.PropertyType)), x.PropertyType ); return Expression.Bind(x, convertedExpr); }); var body = Expression.MemberInit(Expression.New(typeof(Foo)), bindings); var lambdaExpr = Expression.Lambda<Func<Dictionary<string, object>, object>>(body, dictArg); return lambdaExpr.Compile(); }
说明
Convert.ChangeType会自动处理以下逻辑:
- 将装箱的
int拆箱为int - 将
int转换为double - 返回装箱后的
double,最后通过Expression.Convert拆箱为double类型赋值给属性
这样就能避免直接拆箱转换导致的异常。
内容的提问来源于stack exchange,提问作者Node.JS
相关产品推荐
相关产品推荐

