foreach最安全常用,但循环中修改List长度会因版本号校验抛InvalidOperationException;应倒序for删除或用RemoveAll筛选。
直接说结论:用 foreach 最安全、最常用,但别在循环里改 list 的长度(增删元素),否则抛 invalidoperationexception。
为什么 foreach 会崩在 List.Remove() 上
因为 foreach 底层调用的是 List,它内部持有一个版本号(_version)。只要调用 Add()、Remove()、Clear() 等任何修改集合结构的方法,版本号就变,迭代器立刻检测到不一致,直接 throw。
- 错误现象:
System.InvalidOperationException: 集合已修改;枚举操作可能无法执行 - 常见场景:边遍历边过滤,比如“删掉所有空字符串”
- 别这么写:
foreach (var s in list) { if (string.IsNullOrEmpty(s)) list.Remove(s); }
要删元素,改用 for 倒序遍历
倒序是因为索引不会因前面的删除而错位——删掉索引 i 的元素后,原来 i+1 的元素落到 i,但下一轮你本来就要看 i-1,不影响。
- 正确做法:
for (int i = list.Count - 1; i >= 0; i--) { if (string.IsNullOrEmpty(list[i])) list.RemoveAt(i); } - 注意:不能正序
i++,否则跳过下一个元素 - 如果只是筛选,用
list.RemoveAll()更简洁、性能更好
foreach 和 for 各自适合什么场景
不是“哪个更好”,而是“谁更贴合当前动作”。foreach 是只读遍历的语义糖衣;for 是带索引控制的底层工具。
- 用
foreach:只读访问、逻辑清晰、避免索引越界风险,例如日志打印、计算总和 - 用
for:需要索引(比如只处理偶数位)、要修改集合(删/改)、性能敏感(省去迭代器开销) - 性能差异很小,.NET Core 之后
foreach对List已优化成类似for的直访,别为这点微差强行换写法
真正容易被忽略的点是:哪怕你没显式调用 Remove(),只要集合被其他线程或回调改了,foreach 一样会崩。多线程下要么加锁,要么先 ToArray() 快照一份再遍历。











