Iterators and versioning

  • Thread starter Thread starter Guest
  • Start date Start date
G

Guest

I notice that using iterators it is possible to modify a collection whilst
enumerating over it's elements. Is this by design or just a side-effect of
their implementation? Viewing the disassembly for the generated classes I
note that the compiler-generated enumerator has no version checking. Is there
an in-depth analysis of this on the www anywhere?

Thanks

kh
 
I don't understand this question... are you saying while iterating through a
collection, you have a method to modify it?.. if so, the method to modify is
given as part of the collection object, you should not use it while
iterating through the list

VJ
 
To put it another way, in 1.1 it was standard practice to implement
enumerators such that modifications to the collection during iterations would
cause MoveNext() to throw. In 2.0 the enumerators generated by the compiler
for iterator blocks do no such thing. Why is this?

kh
 
kh said:
To put it another way, in 1.1 it was standard practice to implement
enumerators such that modifications to the collection during iterations
would
cause MoveNext() to throw. In 2.0 the enumerators generated by the
compiler
for iterator blocks do no such thing. Why is this?

Because it is, in general, needlessly inefficient.
Either the enumerated class would need to track all enumerators it creates
or else it would have to keep a counter that it incremented on every
modification for the enumerators to check. Either way there is both a space
and time overhead.
Even then it wouldn't handle multi-threads.
 
I just checked the documentation and it explicitly says

" the enumerator is irrecoverably invalidated and the next call to MoveNext
or Reset throws an InvalidOperationException."

SO it would seem that you have to do this to implement IEnumerator properly.

Obviously the compiler cannot implement "yield" in such a way as to support
this behaviour so this must be seen as a documentation bug and the
documentation should be altered to from "throws" to "may throw".

Also the documentation for "Reset" needs to document the possibility of
"NotSupportedException" since "yield" methods don't support it.
 
Nick Hounsome said:
Obviously the compiler cannot implement "yield" in such a way as to support
this behaviour so this must be seen as a documentation bug and the
documentation should be altered to from "throws" to "may throw".

Yeah. I reckon it's actually USEFUL to have enumerators that allow
in-place modification. I mean, you're iterating through a linked list,
and the entire point of a linked list is that changes in one place
don't affect a reader in another.
 
Lucian Wischik said:
Yeah. I reckon it's actually USEFUL to have enumerators that allow
in-place modification. I mean, you're iterating through a linked list,
and the entire point of a linked list is that changes in one place
don't affect a reader in another.

That would be something else entirely.
I was saying that it is an error but that the enumerator might not throw an
exception. You are saying that, in some circumstances, it wouldn't be an
error at all.

In my experience, the only thing that you often want to do to a list whilst
iterating is remove the current element (which is better handled by having a
filter method on the collection that takes a predicate).
 
I notice that using iterators it is possible to modify a collection whilst
enumerating over it's elements.

C# 2.0 iterators provide a code transformation, via a generated state
machine, that turns yield return statements into reentry points for
subsequent calls to MoveNext() on the generated IEnumerator
implementation. This code transformation is useful for things which
aren't 'collections' per se: for example, an infinite stream of
positive integers, or an infinite stream of random numbers.
Is this by design or just a side-effect of
their implementation? Viewing the disassembly for the generated classes I
note that the compiler-generated enumerator has no version checking. Is there
an in-depth analysis of this on the www anywhere?

Iterators are useful for more than simply implementing collection
classes, or otherwise they wouldn't be a useful addition to the
language.

Who spends all their time implementing collection classes? If enough
people did this to warrant the feature, then somebody in the BCL
development team isn't doing their job.

Thus, it's a language feature with a far wider range of applicability.

Personally, whenever I need a collection, I descend from an existing
collection class like Collection<T>. However, I've implemented many
iterators, none of which were over an actual collection, but rather
aggregated data from other places, or synthesised data from some
algorithm.

If the C# 2.0 iterator implementation kept track of some notion of
read-only-ness (and that's not possible anyway, since it doesn't know
where I'm getting the data), I wouldn't be using it at all.

-- Barry
 
In my experience, the only thing that you often want to do to a list whilst
iterating is remove the current element (which is better handled by having a
filter method on the collection that takes a predicate).

Of course, not all enumerations are over a collection.

-- Barry
 
Thanks Barry and Nick. Very useful responses. I need to spend more time with
iterators to really appreciate their power and benefits.

kh
 
Nick said:
I just checked the documentation and it explicitly says

" the enumerator is irrecoverably invalidated and the next call to MoveNext
or Reset throws an InvalidOperationException."

SO it would seem that you have to do this to implement IEnumerator properly.

Obviously the compiler cannot implement "yield" in such a way as to support
this behaviour so this must be seen as a documentation bug and the
documentation should be altered to from "throws" to "may throw".

Also the documentation for "Reset" needs to document the possibility of
"NotSupportedException" since "yield" methods don't support it.

I don't see in what way they don't support it. You can write an
iterator block which checks for modifications (with a version number)
easily enough, can't you? Sure, it's extra work - but it's doable.

Jon
 
Jon Skeet said:
I don't see in what way they don't support it. You can write an
iterator block which checks for modifications (with a version number)
easily enough, can't you? Sure, it's extra work - but it's doable.

You don't get to write the Reset() method and the one that's created for you
throws NotSupportedException even though this is not mentioned in
IEnumerator.Reset (I'm not even sure where it is mentioned!) although
InvalidOperationException IS desecribed as being thrown if the collection is
modified [though I can't see why you shouldn't be able to reset an iterator
on a modified collection]

InvalidOperationException is what you should throw from your iterator block
if you want to do the checking but this is only used to implement MoveNext.
 
Nick said:
You don't get to write the Reset() method and the one that's created for you
throws NotSupportedException even though this is not mentioned in
IEnumerator.Reset (I'm not even sure where it is mentioned!)

The C# spec states that it'll be thrown. Possibly a bad move - not
sure.
although InvalidOperationException IS desecribed as being thrown if the collection is
modified [though I can't see why you shouldn't be able to reset an iterator
on a modified collection]

InvalidOperationException is what you should throw from your iterator block
if you want to do the checking but this is only used to implement MoveNext.

I see - so you can implement *one* of the specified exceptions, just
not all of them.

Jon
 
indeed you can, and the generated enumerator starts to resemble those we
wrote for v1.1. but the documentation is assuming that we all invariably
implement version checking in our iterator blocks..

public IEnumerator<T> GetEnumerator()
{
int version = this.version;
foreach (T item in items)
{
if (version != this.version)
throw new InvalidOperationException("collection has been
modified");
yield return item;
}
}
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Back
Top