C#中DataTable转List推荐反射通用法:属性名与列名不区分大小写匹配,自动处理基础类型及可空类型,跳过不匹配列和DBNull值;LINQ法简洁但需手动处理类型转换和DBNull。

在C#中,将 DataTable 转换为泛型 List 是常见需求,核心思路是:**通过反射获取目标类型 T 的属性,逐行读取 DataTable 的数据并映射赋值**。关键在于字段名与属性名的匹配、类型转换的安全处理,以及性能优化。
使用反射 + 泛型方法(推荐,通用性强)
这是最常用、可复用的方式,适合大多数自定义实体类:
- 要求实体类属性名与 DataTable 列名(
ColumnName)完全一致(不区分大小写更稳妥) - 自动处理基础类型(int、string、DateTime 等)和可空类型(int?、DateTime?)
- 跳过不匹配的列或无法转换的值,避免运行时异常
示例代码:
public static ListToList (DataTable dt) where T : new() { var list = new List (); var properties = typeof(T).GetProperties().ToDictionary(p => p.Name, StringComparer.OrdinalIgnoreCase); foreach (DataRow row in dt.Rows) { var obj = new T(); foreach (var prop in properties.Values) { if (dt.Columns.Contains(prop.Name) && row[prop.Name] != DBNull.Value) { try { var value = Convert.ChangeType(row[prop.Name], prop.PropertyType); prop.SetValue(obj, value); } catch { /* 类型不兼容时跳过该字段 */ } } } list.Add(obj); } return list;}
调用方式:
var users = ToList(dataTable); 使用 LINQ + DataRowExtensions(.NET Framework 3.5+ / .NET Core 3.0+)
如果 DataTable 结构固定、实体简单,可用更简洁的 LINQ 写法:
- 依赖
System.Data.DataSetExtensions(需引用对应 NuGet 包或框架内置)- 适合快速原型或单次转换,可读性高
- 需手动处理类型转换和 DBNull,灵活性略低
示例:
var list = dataTable.AsEnumerable() .Select(row => new User { Id = row.Field("Id"), Name = row.Field ("Name"), BirthDate = row.Field ("BirthDate") }).ToList(); 注意字段映射与类型安全
实际使用中容易出错的点:
- 列名大小写敏感:DataTable 列名默认区分大小写,建议用
StringComparer.OrdinalIgnoreCase或统一转小写比对- DBNull 处理:必须检查
row[colName] != DBNull.Value,否则Convert.ChangeType会抛异常- 可空类型支持:如
int?,要确保源值为 null 或能转为目标基础类型,Field方法内部已处理() - 日期/时间格式:数据库中可能存字符串,需提前清洗或自定义转换逻辑
性能优化小提示
大数据量时可提升效率:
- 预先缓存
PropertyInfo[]和列索引(如dt.Columns.IndexOf("Name")),避免每次循环重复查找- 用
foreach (DataRow row in dt.Rows)比for (int i = 0; i 更高效(DataTable 内部做了优化)- 若确定无 DBNull 且类型严格匹配,可省略 try-catch,但务必确保数据质量
基本上就这些。选反射通用版还是 LINQ 简洁版,取决于你的项目规范、数据稳定性和维护成本。不复杂但容易忽略细节,尤其在跨数据库或历史数据迁移场景下。










