1

Is there a way to remember the position of an enumerator? I want to remember the position of an enumerate, so that I can reset it to a position before the current. I don't want to go back to the beginning so .reset() doesn't help. Btw, is it possible to let the enumerator start eg at the 2. position?

List<string> list = new List<string>(new string[] { "a", "b", "c" });
IEnumerator<string> i = list.GetEnumerator();
i.MoveNext(); richTextBoxOutput.AppendText(i.Current);
IEnumerator<string> t = i; // how do I make a real copy i?
i.MoveNext(); richTextBoxOutput.AppendText(i.Current);
i = t;
i.MoveNext(); richTextBoxOutput.AppendText(i.Current);
John Saunders
  • 160,644
  • 26
  • 247
  • 397
bebo
  • 819
  • 1
  • 9
  • 26
  • Although not a direct answer, you may find this answer useful in resolving your query: http://stackoverflow.com/questions/432600/writing-custom-ienumeratort-with-iterators – Martin Jun 05 '15 at 10:29
  • I have edited your title. Please see, "[Should questions include “tags” in their titles?](http://meta.stackexchange.com/questions/19190/)", where the consensus is "no, they should not". – John Saunders Jun 05 '15 at 11:16

3 Answers3

1

As you already have a List<> why don't you maintain an indexer/counter then use the IEnumerable Skip() extension method (and possibly combine that with Take() followed by ForEach()).

Some possibly useful further info:

Community
  • 1
  • 1
slugster
  • 49,403
  • 14
  • 95
  • 145
0

Do you definitely need an IEnumerator instance? Why not enumerate using the index and store that in your own variable?

var list = new List<string>(new { "a", "b", "c" });

var pos = 2;  // this is the position

richTextBoxOutput.AppendText(list[pos]); 

You can reset at any time with:

pos = (desired position);
James Harcourt
  • 6,017
  • 4
  • 22
  • 42
  • Is it recommended to use an index instead of an enumerator? I think list objects are linked and isn't it faster to follow the links instead of using an index that requires much more time to compute? – bebo Jun 05 '15 at 10:45
  • He has used a List in his question - and IList provides an object indexer which results in an O(1) operation to retrieve a particular item. Why not use that? – James Harcourt Jun 05 '15 at 11:00
  • @bebo, `List` is implemented as a wrapper around an array, which is replaced by a larger array as needed, and `IList` generally is best used with structures that give fast indexing, rather than with linked lists. – Jon Hanna Jun 05 '15 at 11:12
  • There is an interesting comparison between linked lists and List here: http://stackoverflow.com/questions/169973/when-should-i-use-a-list-vs-a-linkedlist – James Harcourt Jun 05 '15 at 11:22
0

Is there a way to remember the position of an enumerator?

Sometimes. It depends on how the enumerator is implemented.

In this case the enumerator is implemented as a mutable struct, which was a performance optimisation that people more often run into when it produces this "freeze position" behaviour in situations where they don't want it. (If you're ever writing a generic class that wraps an implementation of IEnumerable<T> then either hold that reference as the interface type rather than the type itself, or don't have it readonly even if it seems like it should be, if you do you can end up with such a struct enumerator permanently frozen).

Just change your code so that instead of:

IEnumerator<string> i = list.GetEnumerator();
…
IEnumerator<string> t = i;

You have either:

List<string>.Enumerator i = list.GetEnumerator();
…
List<string>.Enumerator t = i;

Or simply:

var i = list.GetEnumerator();
…
var t = i;

Now you have i and t defined in terms of this struct and copying from one to the other copies the struct rather than just the reference to the boxed struct.

This will not work with all enumerators, and for that matter it isn't the best way to deliberately make it available when writing your own enumerator (if you needed to do so you'd be better adding some sort of Clone() or Snapshot() method to an enumerator that was a class rather than a struct), but it will work with List<T>.

A more flexible solution that doesn't depend on such a quirk of implementation would be:

public class SnapshotableListEnumerator<T> : IEnumerator<T>
{
  private readonly IList<T> _list;
  private int _idx;
  private SnapshotableListEnumerator(IList<T> list, int idx)
  {
    _list = list;
    _idx = idx;
  }
  public SnapshotableListEnumerator(IList<T> list)
    : this(list, -1)
  {
  }
  public bool MoveNext()
  {
    // Note that this enumerator doesn't complain about the list
    // changing during enumeration, but we do want to check that
    // a change doesn't push us past the end of the list, rather
    // than caching the size.
    if(_idx >= _list.Count)
      return false;
    ++_idx;
    return true;
  }
  public void Reset()
  {
    _idx = -1;
  }
  public T Current
  {
    get
    {
      if(_idx < 0 || _idx >= _list.Count)
        throw new InvalidOperationException();
      return _list[_idx];
    }
  }
  object IEnumerator.Current
  {
    get { return Current; }
  }
  public void Dispose()
  {
  }
  public SnapshotableListEnumerator<T> Snapshot()
  {
    return new SnapshotableListEnumerator<T>(_list, _idx);
  }
}
public static class SnapshotableListEnumeratorHelper
{
  public static SnapshotableListEnumerator<T> GetSnapshotableEnumerator<T>(this IList<T> list)
  {
    return new SnapshotableListEnumerator<T>(list);
  }
}

Now you can call GetSnapshotableEnumerator() on any implementation of IList<T> and use its Snapshot() method whenever you want a copy of the position within the enumeration.

Jon Hanna
  • 110,372
  • 10
  • 146
  • 251