Sunday, May 29, 2011

C# under the covers -- Enumerators

Following up on the comment stream to an earlier post, a few little bits revealed by the investigations, with a little help from ILSpy:

yield break;

is the equivalent of


inside an iterator function -- by which I mean that any silent exit from the method is compiled to the equivalent of an explicit yield break;, and

foreach(T x in y) { ... }

on exit from the iteration calls the Dispose() method of the IEnumerator<T> it uses implicitly to iterate over the collection y; whereas, of course, explicit use of the enumerator MoveNext() and Current do not.

The behavioural constraints of IEnumerable are weak enough as it is, being silent on whether replayability is part of the contract or not; this inconsistent disposal (with the IDisposable interface being added only to the generic IEnumerator<T> and not the .net 1.x vanilla version) makes guessing behaviours even worse when using LINQ methods like Skip which does the dispose via using when the iteration is exhausted

and Take, which does it implicitly by the foreach route before the iterator has been completely drained

There are probably similar wonders waiting to be unearthed in the F# collections APIs as well.

No comments :