为何遍历Dictionary时移除元素可正常运行?
为什么遍历Dictionary时移除元素不报错,List却会?
你遇到的这个差异,本质是List和Dictionary的枚举器(Enumerator)实现逻辑不同:
List的枚举器是「失败快速」设计
List的枚举器会维护一个内部版本号,每次对List执行添加、移除等修改操作时,这个版本号都会递增。在遍历的MoveNext步骤中,枚举器会检查当前集合的版本号是否和枚举器创建时的版本号一致,如果不一致,就会抛出InvalidOperationException——这就是你看到List遍历移除时报错的原因:第一次移除后集合版本号改变,下一次遍历就触发了检查。
Dictionary的枚举器不严格遵循失败快速
Dictionary基于哈希表实现,它的枚举器遍历的是哈希表的桶结构。当你调用Remove时,它只是把对应的桶标记为「已删除」,不会立即重构整个哈希表(除非后续需要扩容等操作)。枚举器在遍历过程中会自动跳过这些标记为删除的桶,所以不会触发异常。
需要注意:这种行为属于未定义行为,.NET官方文档并没有保证遍历Dictionary时修改元素一定不会出错,只是在你测试的这种逐个移除的场景下刚好能运行。如果遍历过程中同时有添加操作,或者哈希表触发了扩容,还是可能出现问题。
另外你测试的遍历myDict.Keys的情况,Keys集合是Dictionary的动态视图(不是静态快照),它的枚举器逻辑和Dictionary本身的枚举器一致,所以也不会报错,但同样属于未定义行为。
正确的遍历修改集合方式
不管是List还是Dictionary,都不应该在遍历原集合时直接修改,推荐的做法是:
- 遍历集合的副本,比如:
// List遍历副本移除 foreach (var item in myList.ToList()) { myList.Remove(item); } // Dictionary遍历Keys的副本移除 foreach (var key in myDict.Keys.ToList()) { myDict.Remove(key); } - 对于List,也可以用for循环从后往前遍历移除,避免索引偏移问题:
for (int i = myList.Count - 1; i >= 0; i--) { myList.RemoveAt(i); }
内容的提问来源于stack exchange,提问作者Scorb
相关产品推荐
相关产品推荐

