在foreach循环迭代DataTable时修改集合触发报错,如何不退出当前循环完成表实例更新?
嗨,这个问题我太熟悉了——本质上是**枚举器的“失效规则”**在搞鬼:foreach依赖的枚举器会监测原集合的变化,一旦集合被修改(比如你这里的Delete()+AcceptChanges()会直接移除行,改变集合元素数量),枚举器就会抛出“集合已被修改”的异常,阻止后续操作。
要满足你“不退出当前foreach、不开新循环”的要求,其实只要做一个小改动:让外层foreach遍历原Rows集合的副本,而不是原集合本身,这样原集合的修改就不会影响枚举过程了。
修改后的代码示例
// 关键:用ToList()生成Rows的副本,遍历副本而非原集合 foreach (DataRow dtRow in dtTable.Rows.Cast<DataRow>().ToList()) { // 部分代码 // 你原来的倒序删除逻辑可以保留,倒序遍历本身就不会因为索引偏移出问题 for(int i = dtTable.Rows.Count-1; i >= 0; i--) { DataRow dr = dtTable.Rows[i]; if (dr["Column1"].ToString().Contains("some text")) dr.Delete(); } dtTable.AcceptChanges(); // 部分代码2 }
为什么这样可行?
dtTable.Rows.Cast<DataRow>().ToList()会把原DataTable的行一次性复制到一个独立的List<DataRow>中,外层foreach遍历的是这个副本。- 当你在内部修改原
dtTable.Rows(删除行、执行AcceptChanges())时,副本列表完全不受影响,所以枚举器不会触发异常,你可以顺利执行到“部分代码2”。
更简洁的写法(可选)
如果你的删除逻辑可以用LINQ简化,也可以直接一次性收集要删除的行(同样依赖ToList()生成副本),避免写for循环:
foreach (DataRow dtRow in dtTable.Rows.Cast<DataRow>().ToList()) { // 部分代码 // 用LINQ筛选要删除的行,ToList()生成副本后遍历删除 foreach (DataRow dr in dtTable.Rows.Cast<DataRow>().Where(dr => dr["Column1"].ToString().Contains("some text")).ToList()) { dr.Delete(); } dtTable.AcceptChanges(); // 部分代码2 }
这个写法更简洁,而且同样不会触发枚举异常——因为我们遍历的是筛选后的行副本,原集合的修改和它无关。
最后小提醒:AcceptChanges()会把标记为删除的行从DataTable中彻底移除,如果你只是想标记删除而不立即移除,可以暂时不调用它(但外层遍历副本的逻辑依然适用)。
内容的提问来源于stack exchange,提问作者Foertsch
相关产品推荐
相关产品推荐

