I'd like to create a copy of an IEnumerator<T>
so that I can restart the enumeration process from a particular location in the collection. Clearly, there is no benefit to doing so for collections that implement IList
, since we can remember the index of interest.
Is there a clever way to accomplish this task using a combination of yield
statements and Linq functions? I could not find a suitable Clone()
method to copy the enumerator, and would like to avoid using Enumerable.Skip()
to reposition a new enumerator to the desired resumption point.
Also, I'd like to keep the solutions as generic as possible, and not have to depend on state from any concrete collections.
This is completely not an answer, but the thought experiment I found interesting...if you've got a yield-based IEnumerable, I suppose you know it's all compiler-generated magic. If you have such a beast, you could do something like this... ;)
JerKimball had an interesting approach. I try to take it to the next level. This uses reflection to create a new instance and then sets the values on the new instance. I also found this chapter from C# in Depth to be very useful. Iterator block implementation details: auto-generated state machines
So what you really want is to be able to resume an iteration later, am I correct? And cloning the enumerator or collection is how you think you'd do such a thing?
You could make a class which wraps an IEnumerable, and exposes a custom enumerator which, internally, clones the inner IEnumerable, and then enumerates over that. Then, using GetEnumerator() would give you an enumerator which could be passed around.
This would create an extra copy of the IEnumerable for each Enumerator "in flight," but I think it would meet your needs.
The best you could do is write something that keeps a buffer (perhaps a
Queue<T>
) of the data consumed from one and not the other (which would get messy/expensive if you advanced one iterator by 1M positions, but left the other alone). I really think you would be better off rethinking the design, though, and just usingGetEnumerator()
(i.e. anotherforeach
) to start again - or buffer the data (if short) in a list/array/whatever.Nothing elegant built in.
Update: perhaps an interesting alternative design here is "PushLINQ"; rather than clone the iterator, it allows multiple "things" to consume the same data-feed at the same time.
In this example (lifted from Jon's page) we calculate multiple aggregates in parallel:
There's no general way to do this, since an iEnumerable may depend upon arbitrary aspects of system state which cannot be detected via Reflection or any other means. For example, a PaperTapeReader class might implement an enumerator which reads characters from the tape until the sensor indicates there's no more tape in the machine. The state of such an enumerator would be the physical location of the tape, which might be impossible to restore programmatically.
Given an iEnumerable, it would be possible to produce two or more iEnumerables, each of which would act like either the original or clone thereof. MoveNext requests for the one that was 'furthest along' would read new data from the original iEnumerable and buffer it for the others. Unless the original iEnumerable supports such 'hook' functionality, however, I don't think there'd be any way to latch onto its data as it comes in.
Do you want to be able to save the state, continue the enumeration, then return to the saved state, or do you want to simply be able to enumerate, do some other stuff, then continue the enumeration?
If it's the latter, something like the following might work:
Now you can do: