想像してみてください
foreach(var item in enumerable)
列挙可能な項目が変わります。現在の foreach に影響しますか?
例:
var enumerable = new List<int>();
enumerable.Add(1);
Parallel.ForEach<int>(enumerable, item =>
{
enumerable.Add(item + 1);
});
それは永遠にループしますか?
想像してみてください
foreach(var item in enumerable)
列挙可能な項目が変わります。現在の foreach に影響しますか?
例:
var enumerable = new List<int>();
enumerable.Add(1);
Parallel.ForEach<int>(enumerable, item =>
{
enumerable.Add(item + 1);
});
それは永遠にループしますか?
通常、例外をスローする必要があります。List<T>
GetEnumerator()の実装は、 (Reflector から) メソッドが次のようなEnumerator<T>
オブジェクトを提供します。MoveNext()
public bool MoveNext()
{
List<T> list = this.list;
if ((this.version == list._version) && (this.index < list._size))
{
this.current = list._items[this.index];
this.index++;
return true;
}
return this.MoveNextRare();
}
private bool MoveNextRare()
{
if (this.version != this.list._version)
{
ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion);
}
this.index = this.list._size + 1;
this.current = default(T);
return false;
}
はlist._version
、リストを変更する操作ごとに変更 (増分) されます。
列挙子の性質に依存します。コレクションが変更されると、それらの多くは例外をスローします。
たとえば、列挙中にコレクションが変更されList<T>
た場合にスローします。InvalidOperationException
IEnumerable
[and --non -generic names will refer to both]に関する Microsoft のドキュメントでは、これらのインターフェイスを実装するオブジェクトが変更されるたびに、以前に生成された[ ]IEnumerable<T>
のインスタンスを無効にすることを推奨しています。. Microsoft のドキュメントにはこのスタンスからの変更が記載されていませんが、実際の の実装はより緩いルールに従っているようです。「賢明に」動作できない場合はスローする必要がありますIEnumerator
IEnumerator<T>
InvalidOperationException
IEnumerable
IEnumerator
InvalidOperationException
. 残念ながら、その規則は明示的に述べられておらず、クラスの動作から推測されているため、「賢明な」動作が正確に何を意味するかは明確ではありません。
私が知っているすべての Microsoft クラスは、次の基準を満たさない場合、コレクションが変更されたときに例外をスローします。
コレクションが変更された場合でも上記の基準を満たすことができるかどうかを (例外をスローすることなく) 報告できる何らかの手段があると便利です。たとえば、特定の基準を満たすすべてのアイテムを削除しようとするときに非常に役立つからです。
これは、IEnumerable の実装方法に完全に依存します。
リストでは、IllegalOperationException がスローされます。ただし、IEnumarables のこの動作に依存しないでください。一部のループは無限にループし、すぐに OutOfMemoryexception をスローします。