Does the foreach loop in C# guarantee an order of evaluation?
For arrays (note that System.Array
implements IEnumerable
), it will access elements in order. For other types (IEnumerable
, or having GetEnumerator
), it accesses elements in the order provided, through alternating MoveNext
and Current
calls.
The standard states (ECMA-334 §13.9.5):
"The order in which foreach traverses the elements of an array, is as follows: For single-dimensional arrays elements are traversed in increasing index order, starting with index 0 and ending with index Length – 1. For multi-dimensional arrays, elements are traversed such that the indices of the rightmost dimension are increased first, then the next left dimension, and so on to the left."
foreach
is built on top of IEnumerable<T>
The contract for the enumerator on MSDN says
Initially, the enumerator is positioned before the first element in the collection. ... Therefore, you must call MoveNext to advance the enumerator to the first element of the collection before reading the value of Current.
Current returns the same object until MoveNext is called. MoveNext sets Current to the next element.
So if the underlying collection has clear 'first' element, and each element has a clear 'next' element, as is the case for arrays, lists and so on, then you can expect that the foreach
to behave logically and stably. If it is something like a set, which has no first or next sequence, then it may behave in an unstable manner, though presumably without changing the IEnumerable's state even collections which have no defined order will be consistent, as making them inconsistent would be more work!
For what it's worth, you can look up a lot of this in Reflector. In mscorlib
, System.Array implements IEnumerable
(as mentioned), and Array#GetEnumerator
returns an ArrayEnumerator
. Here's the body of ArrayEnumerator#MoveNext
:
public bool MoveNext()
{
if (this._complete)
{
this.index = this.endIndex;
return false;
}
this.index++;
this.IncArray();
return !this._complete;
}
That's obviously one example, but the answer is: it's up to the implementer and you can find out most of the way they work experimentally, or by inspecting the source, in some cases.