If one says var MyList = SomeStringCollection.ToList()
; MyEnumerator = MyList.GetEnumerator();, then the type of
MyListwill be
List, and the type of
MyEnumeratorwill be
List.Enumerator, which is a *value type* that implements
IEnumerator, and will thus exhibit *value semantics*. If the type of
MyListhad been
IEnumerable, the return type of
MyList.GetEnumerator()would have been
IEnumerator`, which has reference semantics.
This difference could be important if e.g. one had a method which read five items from an IEnumerator<String>
. If one calls such a method multiple times with a variable of type IEnumerator<String>
, each call will read the next five items from the enumeration. By contrast, if one calls such a method multiple times with a value type like List<String>.Enumerator
, each call will copy the enumeration state to a new heap object which implements IEnumerator<T>
, pass that object to the method (which will read five items through it), and then abandon that object and its associated space (leaving the enumeration state associated with the original variable unaffected).
Note that in probably 99% of cases, either value semantics or reference semantics would be equally acceptable in an enumerator; in the majority of those, value semantics would make code run faster, and only very rarely would it make code run meaningfully slower. Further, there are some cases where knowing that an enumerator will behave as a value type could be semantically useful (e.g. if one wanted to be able to repeatedly re-enumerate part of a collection). On the other hand, it's probably a good idea for code which uses implementations of IEnumerator<T>
to be aware of what kind of implementation they may be using.