DataTable 创建和列定义怎么写才不踩坑
直接 new
DataTable()没问题,但列类型必须明确指定,否则后续赋值
DBNull.Value或类型不匹配时会抛异常(比如把
"123"往
int列里塞)。别用
dt.Columns.Add("Name") 这种无类型写法——它默认是 string,但掩盖了设计意图,后期改类型或导出到数据库时容易出错。 推荐写法:
dt.Columns.Add("Id", typeof(int)) 或 new DataColumn("Name", typeof(string))
注意:typeof(string)比
System.Type.GetType("System.String") 更简洁、更安全(后者字符串拼错就 null)
如果列可能为空,int?不被 DataTable 原生支持,得用
typeof(int)+ 允许
DBNull.Value,读取时用
row["Id"] == DBNull.Value ? null : (int?)row["Id"]
添加行为什么用 ImportRow
而不是 Rows.Add(dr)
当你从另一个
DataTable里取
DataRow(比如
src.Select("...")[0]),再往目标表加时,直接 dst.Rows.Add(dr)会报错:“该行已经属于另一个表”。这是 DataTable 的所有权机制导致的——
DataRow绑定在创建它的表上。 正确做法是:
dst.ImportRow(dr),它复制数据和状态(包括
RowState),不破坏原行归属
Rows.Add(object[])也安全,比如
dst.Rows.Add(srcRow.ItemArray),但丢失原始
RowState和表达式列值 批量插入场景下,
ImportRow比
Merge快 100 倍(尤其多表合并时)
Select()
和 DefaultView.RowFilter
性能差在哪
查数据别一上来就用
dt.DefaultView.RowFilter = "Age > 25"; dt.DefaultView.ToTable()。这会强制构建完整 DataView 并生成新表,内存和 CPU 开销大;而
dt.Select("Age > 25") 是纯内存扫描,返回 DataRow[],快且轻量。 小数据量(Select() 通常快 10–30 倍
Select()不支持 LIKE 通配(只能用
column LIKE 'A%',且区分大小写),也不支持函数如
UPPER();需要这些就得切到
DefaultView若要复用筛选逻辑,建议封装为方法并缓存
DataView实例,而非反复
ToTable()
DataTable 大了以后卡顿,怎么救
界面绑定后还一边循环
Rows.Add()一边刷新控件?那是典型“边绑边填”反模式。GridControl 等控件监听
Rows.CollectionChanged,每加一行都触发重绘,1000 行可能卡死几十秒。 务必先解绑:
grid.DataSource = null,填完再赋值 大数据量(>1w 行)优先考虑分页或虚拟滚动,而不是全量加载到 DataTable 避免在循环里反复调用
row["ColName"]——改用
row[index](列序号)或提前缓存
DataColumn对象 真要高性能遍历,用
dt.CreateDataReader(),它比
foreach (DataRow r in dt.Rows)少一半对象分配开销
最常被忽略的一点:DataTable 本质是内存中的关系表,不是 ORM 替代品。它适合中等规模、结构稳定、需离线操作的场景;一旦涉及复杂关联、实时计算或百万级数据,该换
System.Linq.Expressions+
List<t></t>或实体类集合了。
