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

如何无需for循环且以最快方式将属性匹配的DataTable转为List<T>

不使用for循环将DataTable转换为List的方法及最优实现

核心逻辑

避免显式for循环的本质是借助框架内置迭代器、反射或表达式树来隐式处理DataTable的每一行,同时通过预编译、缓存等手段追求最高转换性能。

方法1:LINQ + 反射(简洁易实现)

利用LINQ的AsEnumerable()遍历行集合,结合反射自动匹配属性与列名完成映射,无需手写显式for循环。适合小数据量场景。

示例代码:

public static List<T> DataTableToList<T>(DataTable dt) where T : new()
{
    return dt.AsEnumerable()
             .Select(row => 
             {
                 T model = new T();
                 foreach (var prop in typeof(T).GetProperties())
                 {
                     if (dt.Columns.Contains(prop.Name))
                     {
                         var cellValue = row[prop.Name];
                         prop.SetValue(model, cellValue == DBNull.Value ? null : cellValue);
                     }
                 }
                 return model;
             })
             .ToList();
}

方法2:表达式树缓存映射委托(高性能通用方案)

反射的性能瓶颈在于重复调用SetValue,通过表达式树提前构建属性赋值的委托并缓存,后续转换直接调用委托,避免重复反射开销,这是通用场景下最快的实现方式。

示例代码:

public static class DataTableConverter
{
    private static readonly Dictionary<Type, Delegate> _converterCache = new Dictionary<Type, Delegate>();

    public static List<T> ToList<T>(DataTable dt) where T : new()
    {
        var converter = GetRowConverter<T>(dt);
        return dt.AsEnumerable().Select(row => converter(row)).ToList();
    }

    private static Func<DataRow, T> GetRowConverter<T>(DataTable dt) where T : new()
    {
        Type modelType = typeof(T);
        if (_converterCache.TryGetValue(modelType, out var cachedDelegate))
        {
            return (Func<DataRow, T>)cachedDelegate;
        }

        // 构建参数表达式:DataRow row
        var rowParam = Expression.Parameter(typeof(DataRow), "row");
        // 构建实例创建表达式:new T()
        var newInstanceExpr = Expression.New(modelType);
        var propertyBindings = new List<MemberBinding>();

        foreach (var prop in modelType.GetProperties())
        {
            if (!dt.Columns.Contains(prop.Name)) continue;

            // 构建row[列名]的表达式
            var cellValueExpr = Expression.Property(rowParam, "Item", Expression.Constant(prop.Name));
            // 处理DBNull到null的转换
            var safeValueExpr = Expression.Condition(
                Expression.Equal(cellValueExpr, Expression.Constant(DBNull.Value)),
                Expression.Constant(null, prop.PropertyType),
                Expression.Convert(cellValueExpr, prop.PropertyType)
            );
            // 绑定属性赋值
            propertyBindings.Add(Expression.Bind(prop, safeValueExpr));
        }

        // 构建实例初始化表达式
        var initExpr = Expression.MemberInit(newInstanceExpr, propertyBindings);
        // 编译为委托并缓存
        var converter = Expression.Lambda<Func<DataRow, T>>(initExpr, rowParam).Compile();
        _converterCache[modelType] = converter;
        return converter;
    }
}

首次构建表达式树会有轻微开销,后续转换性能接近手写硬编码赋值,适合大数据量的通用类型转换。

方法3:手动属性映射(固定类型最优性能)

如果转换的类型固定,直接通过Field<T>方法手动映射属性,性能达到原生水平,但缺乏通用性。

示例代码:

public static List<User> DataTableToUsers(DataTable dt)
{
    return dt.AsEnumerable()
             .Select(row => new User
             {
                 Id = row.Field<int>("Id"),
                 Username = row.Field<string>("Username"),
                 CreateTime = row.Field<DateTime>("CreateTime")
             })
             .ToList();
}

总结

  • 轻量通用场景:选LINQ+反射,代码简洁无需手动配置。
  • 高性能通用场景:选表达式树缓存方案,兼顾通用性与速度,是最优通用实现。
  • 固定类型场景:选手动映射,性能最优但仅适用于特定类型。

内容的提问来源于stack exchange,提问作者jj243

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 15:03:25