foreach真正是靠迭代器实现的 https://blog.csdn.net/u013986317/article/details/85628565
1.不能在在循环体中删除或增加元素;
原因:在执行foreach循环时,集合内部有一个变量用于记录集合的版本,这个版本值会在元素个数发生改变的所有操作中发生修改,每次遍历时会检测这个变量有无发生变化,如果发生变化,则抛出异常。
2.不能在循环内部修改元素(无论值类型还是引用类型);
原因是,在遍历IEnumerator<T>的过程中,把当前的元素(即Current)暴露为只读属性。
3.可以在循环内部修改对象的属性。
解释:
以ArrayList为例,数组实现了IEnumerable接口。
必须实现IEnumerable接口的方法GetEnumerator();
GetEnumerator方法需要返回一个实现了IEnumerator接口的对象。所以ArrayListEnumeratorSimple必须实现IEnumerator接口
ArrayListEnumeratorSimple的构造方法:(注意这个version)
ArrayListEnumeratorSimple类分别实现了IEnumerator接口中的方法,IEnumerator接口有三个方法:
注意 Current只有get属性
ArrayListEnumeratorSimple类中对这三个方法的实现:
在ArrayListEnumeratorSimple的构造方法中看到,迭代器在一开始就记录了元素的版本version,在所有会对集合元素数量有变化的地方,都会更新版本(version++),如Add,Clear等等:
而在执行MoveNext方法开始遍历集合时,一进来就对version进行了对比。如果发现版本发生变化,则抛出异常:集合已修改...
这就解释了第一个注意点。
关于第二点, “不能在循环内部修改元素(无论值类型还是引用类型);”,很明显,当前遍历到的元素Current 只有get属性,所以只能读,不能更改: