Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When items change while being enumerated does it affects the enumeration?

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?

like image 776
Jader Dias Avatar asked Jul 11 '09 21:07

Jader Dias


3 Answers

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.

like image 108
Kenan E. K. Avatar answered Sep 21 '22 15:09

Kenan E. K.


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.

like image 45
mmx Avatar answered Sep 20 '22 15:09

mmx


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:

  1. Any item which exists unmodified throughout an enumeration will be returned exactly once.
  2. 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".
  3. 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.

like image 23
supercat Avatar answered Sep 19 '22 15:09

supercat