This is part 10/17 of my Exploring the .NET CoreFX series.
The .NET Core’s System.Collections.Immutable.ImmutableArray
provides two enumerators. The first has been highly tuned for speed, and the second is a fallback for compatibility when it is required.
The high-performance enumerator uses the following performance optimizations:
- The enumerator is a
struct
, rather than aclass
, so that it is stack-allocated rather than heap-allocated. - The enumerator does not implement
IEnumerator
orIEnumerator
, as this would require it to implementIDisposable
. By not implementingIDisposable
the iterator will inline duringforeach
loops.. - The enumerator does not use range checks in
Enumerator.Current
; it requires on .NET’s array range checks to throw an exception instead.
The high-performance enumerator is called ImmutableArray.Enumerator
, which is returned by ImmutableArray.GetEnumerator()
:
|
|
Note that this method is not defined by any interface ImmutableArray
implements, but it will be used by foreach
. This is because foreach
is pattern-based rather than interface-based. What this means is that foreach
does not require an object to implement IEnumerable
as long as it implements a method named GetEnumerator()
. Similarly, the returned object is not required to implement IEnumerator
as long as it implements a Current
property and a MoveNext()
method.
The result of all this work is that foreach
over an ImmutableArray
is just as efficient as a hand-written for
loop.
Recommendations
- To improve the performance of foreach, consider writing a specialized, struct-based enumerator in addition to the traditional one.