问题
Imagine that during a
foreach(var item in enumerable)
The enumerable items change. It will affect the current foreach?
Example:
var enumerable = new List<int>();
enumerable.Add(1);
Parallel.ForEach<int>(enumerable, item =>
{
enumerable.Add(item + 1);
});
It will loop forever?
回答1:
Generally, it should throw an exception.
The List<T>
implementation of GetEnumerator() Provides an Enumerator<T>
object whose MoveNext()
method looks like this (from Reflector):
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;
}
The list._version
is modified (incremented) on each operation which modifies the List.
回答2:
Depends on the nature of the enumerator. Many of them throw exception when the collection changes.
For instance, List<T>
throws an InvalidOperationException
if the collection changes during enumeration.
回答3:
This fully depends on how IEnumerable is implemented.
With a List, It will throw an IllegalOperationException. But don't rely on this behaviour for IEnumarables. Some loop endless and will throw an OutOfMemoryexception quickly.
回答4:
Microsoft's documentation for IEnumerable
[and IEnumerable<T>
--non-generic names will refer to both] recommends that any time an object implementing those interfaces is changed, it should invalidate any instances of IEnumerator
[IEnumerator<T>
] which it has previously produced, causing them to throw InvalidOperationException
on future access attempts. Although nothing in Microsoft's documentation has documented any change from this stance, their actual implementations of IEnumerable
seem to follow a looser rule, which is that an IEnumerator
should not behave nonsensically if the underlying collection is modified; it should throw InvalidOperationException
if it can't behave "sensibly". Unfortunately, since that rule is not explicitly stated but rather inferred from the behavior of their classes, it's not clear what exactly "sensible" behavior should mean.
All of the Microsoft classes that I know of will will throw an exception when a collection is changed if they cannot meet the following criteria:
- Any item which exists unmodified throughout an enumeration will be returned exactly once.
- An item which is added or deleted during enumeration shall be returned at most once, but if an object is removed and re-added during enumeration, each re-addition may be regarded as creating a new "item".
- If a collection guarantees to return things in a sorted sequence, that guarantee must be met even if items are inserted and removed [e.g. if an "Fred" is added to an alphabetically-sorted list, and "George" has already been enumerated, "Fred" must not appear during that enumeration].
It would be helpful if there were some means via which collections could report whether they can satisfy the above criteria (without throwing exceptions) even when modified, since they can be very useful when trying to e.g. remove all items which meet a certain criterion.
来源:https://stackoverflow.com/questions/1114735/when-items-change-while-being-enumerated-does-it-affects-the-enumeration