You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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会自动处理以下逻辑:

  1. 将装箱的int拆箱为int
  2. 将int转换为double
  3. 返回装箱后的double,最后通过Expression.Convert拆箱为double类型赋值给属性

这样就能避免直接拆箱转换导致的异常。

内容的提问来源于stack exchange,提问作者Node.JS

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 08:38:10