Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why the Reset() method on Enumerator class must throw a NotSupportedException()?

Tags:

c#

.net

From what I saw on http://csharpindepth.com/Articles/Chapter6/IteratorBlockImplementation.aspx, and article by Jon Skeet, the c# specification itself says that. What would be the reason?

like image 262
devoured elysium Avatar asked Sep 23 '09 19:09

devoured elysium


2 Answers

That's not how I read the C# spec [Word doc]. Section 10.14.4 "Enumerator objects", states:

...[E]numerator objects do not support the IEnumerator.Reset method. Invoking this method causes a System.NotSupportedException to be thrown.

However, this section (and statement) is specific to "enumerator objects", which is defined as:

When a function member returning an enumerator interface type is implemented using an iterator block, invoking the function member does not immediately execute the code in the iterator block. Instead, an enumerator object is created and returned.

In other words, an "enumerator object" is a compiler generated IEnumerator1. There's no restrictions on every IEnumerator, just the ones generated from iterator blocks (aka yield).

As for why? I'd suspect because it's somewhat impossible to do in the general case - without saving every value and the consequent memory limitations of that. Combine that with the fact that IEnumerator.Reset() is rarely used (when's the last time that you Reset an enumerator?) and that MSDN specifically calls out that it need not be implemented:

The Reset method is provided for COM interoperability. It does not necessarily need to be implemented; instead, the implementer can simply throw a NotSupportedException.

and you get to cut out a lot of complexity without anyone really noticing.

As for requiring that it throw2, I suppose it's just simpler than letting the implementor decide. IMO, it's a bit much to require the throw - there may be reasonable cases that a compiler (or other implementation1) could generate a Reset method for, but I don't see it as being a real problem either.

1 Technically, the spec leaves open the possibility of other implementations:

An enumerator object is typically an instance of a compiler-generated enumerator class that encapsulates the code in the iterator block and implements the enumerator interfaces, but other methods of implementation are possible.

but I'm not aware of any other concrete implementations. Regardless, to be compliant, other implementations of an "enumerator object" would have to throw NotSupportedException as well.

2 Nitpicker's corner: I think there may be some quibble even in the "requirement" to throw. The spec, in not using the preferred "MUST, SHOULD, MAY" verbiage, leaves it a bit open. I read "causes" more as a note of implementation - not a requirement. Then again, I haven't read the entire spec, so perhaps they define these terms a bit more or are more explicit somewhere else.

like image 173
Mark Brackett Avatar answered Oct 24 '22 09:10

Mark Brackett


It is impossible to support properly in all sequences; many are once only (network streams, etc). And if you can't rely on it all the time, it is useless, as the abstraction is broken. Sure you could have an IResettableEnumerator, but Reset() on IEnumerator doesn't work. Frankly, it was a mistake (IMO).

I suspect it would also have made iterator blocks even more complicated (they are currently one of the two most complex parts of the compiler; I can't remember which is "top"; them, or anonymous methods / captured variables).

like image 25
Marc Gravell Avatar answered Oct 24 '22 11:10

Marc Gravell