12

let's have this code :

class MyList : IEnumerable, IEnumerator
{
    int[] A = { 1, 2, 3, 4, 5 };
    int i = -1;

    #region IEnumerator Members

    public object Current
    {
        get { return A[i]; }
    }

    public bool MoveNext()
    {
        i++;
        return i < 5;
    }

    public void Reset()
    {
        i = -1;
    }

    #endregion

    #region IEnumerable Members

    public IEnumerator GetEnumerator()
    {
        return (IEnumerator)this;
    }

    #endregion
}

And In Main Method :

MyList list = new MyList();
foreach (int i in list)
{
    Console.WriteLine(i);
}

foreach (int i in list)
{
    Console.WriteLine(i);
}

Why the second foerach doesn't work ?? and the "i" doesn't initialize again ??

Is that true : Reset method should be called automatically before foreach is executed ??

why it doesn't call here ??

Farah_online
  • 561
  • 1
  • 9
  • 20

6 Answers6

11

Reset is redundant; so much so that it is a requirement in the language spec for iterator blocks to throw an exception on Reset. The correct thing to do is simply dispose and release the old iterator, and call GetEnumerator again. Or better: avoid having to read it twice, since not all data is repeatable.

Chris Taylor
  • 52,623
  • 10
  • 78
  • 89
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • 2
    Do you have a link to where it says that an exception should be thrown? I'd like to read up on that. – John Mills Dec 02 '11 at 01:57
  • Hmmm... found [this](http://msdn.microsoft.com/en-us/library/system.collections.ienumerator.reset.aspx). Apparently `Reset` only exists for COM interoperability. Seems to me that it should never have been defined on the interface in the first place. – John Mills Dec 02 '11 at 02:03
  • Reset wouldn't be redundant if it were better defined. If there was a 'general' IEnumerable, and an IMultipassEnumerable which inherited from it and returned an IMultipassEnumerator, which implemented Reset, then it could have the semantics that repeated enumerations using the same enumerator would be guaranteed to return the same results, even if the collection was changed; by contrast, if one attempts to use two separate enumerators once each, they could very well return different results. – supercat Dec 23 '11 at 18:07
  • @supercat I don't disagree, but: there isn't :p – Marc Gravell Dec 23 '11 at 18:16
8

The IEnumerable and IEnumerator should generally be separate classes, and except in the case of enumerators that always return empty or always return the same item, the GetEnumerator method must always return a new instance of an IEnumerator.

There isn't much point to IEnumerator.Reset; for-each loops don't use it, and consumers of an IEnumerable/IEnumerator can't use it unless they know what the enumerable type is, in which case they could use the actual type rather than the interface.

Tamas Ionut
  • 4,240
  • 5
  • 36
  • 59
supercat
  • 77,689
  • 9
  • 166
  • 211
  • And if somebody implements the MoveNext method in such a way that when it comes to return with false, before this call Reset is a bad practice? And if so, why they put the Reset into the interface? – dvjanm Oct 20 '13 at 07:54
  • @jannagy02: There are a number of aspects of .NET which should be considered vestigial products of evolution/or compromise. Some were never well thought out; others may have been sensible and useful parts at one stage in the design, but rendered useless when other parts of the design reworked. Sometimes the things evolve through the design process, even those involved in a design might not remember which parts were thought through and which ones were thrown together. – supercat Oct 21 '13 at 15:05
4

Reset is not called by foreach. Looking at your Main method in Reflector confirms this.

The .NET classes, like ArrayList, actually return a new instance of a class that implements IEnumerator.

For example ArrayList implements IEnumerable, and its GetEnumerator method looks like this:

public virtual IEnumerator GetEnumerator()
{
    return new ArrayListEnumeratorSimple(this);
}

so there is no need to worry about calling Reset since every foreach uses a new instance of the enumerator.

For a complete example showing the implementation of IEnumerable and a separate class implementing IEnumerator, you can look at the documentation for IEnumerable.

Jeff Ogata
  • 56,645
  • 19
  • 114
  • 127
2

This also works:

public bool MoveNext()
{
        if(i < 5)
        {
            i++;
            return true;
        }
        else
        {
            i = -1;
            return false;
        }
}
Mikolaj
  • 21
  • 1
0

While I agree with the comments regarding vestigial pieces and that Reset() is not used by framework code and generator blocks do indeed throw exceptions, I don't agree that it's completely useless. My understanding as to why generator blocks throw exceptions on reset is because of concerns over any built up state. The very nature of generator blocks make them particularly il-suited for resettable operations, but that doesn't mean a resettable enumerator is bad or poorly designed.

Consider a complex enumeration, built up by composing hundreds of different 'decorator' enumerators. The cost of constructing such an object graph is non-negligible. Now also consider the source of this complex enumeration is dynamic, but for processing reasons, snapshot semantics are required. In such a scenario, we may create this 'enumerator stack' and upon the first MoveNext() call, a snapshot of the source is taken. The complex enumeration/projection/etc is performed and results obtained. Now we wish to perform this operation again, starting at the beginning. Reset() provides a mechanism for this entire enumerator stack to re-initialize to it's starting state without having to re-construct the entire object graph. Further, it permits us to keep the construction of the object graph separated from the consumer who needs to run this complex enumeration multiple times.

I've found many uses for using resettable enumerators and it is almost always in relation to some sort of data feed that requires complex/composable enumerator decorators. In many of these decorators, the call to Reset() is simply passed to the wrapped enumerator instance, but in others, some minor work is performed such as zeroing out a running sum, perhaps restarting a start time, or re-fetching the source snapshot of your data.

Below is an example of a source enumerator that downloads a document an a list and then enumerates over that list. Resetting the enumerator causes the list to be re-downloaded. Further, a decorator enumerator is defined that could be used to wrap the list enumerator (or any enumerator) to project the items of the enumerator. I called it SelectEnumerator since it performs the same role as Enumerable.Select

// excuse the poorly named types
public class ListDownloaderEnumerator<T> : IEnumerator<T>
{
    private int index = -1;
    private readonly string url;
    private IReadOnlyList<T> items;
    public ListDownloaderEnumerator(string url)
    {
        this.url = url;
    }
    public bool MoveNext()
    {
        // downloading logic removed for brevity
        if (items == null) download(url);
        index = index + 1;
        return index < items.Count;
    }
    public void Reset()
    {
        index = -1;
        items = null;
    }
    // other parts of IEnumerator<T>, such as Current
}

public class SelectEnumerator<T, TResult> : IEnumerator<T>
{
    private readonly IEnumerator<T> enumerator;
    private readonly Func<T, TResult> projection;
    public SelectEnumerator(IEnumerator<T> enumerator, Func<T, TResult> projection)
    {
        this.enumerator = enumerator;
        this.projection = projection;
    }
    public bool MoveNext()
    {
        return enumerator.MoveNext();
    }
    public void Reset()
    {
        enumerator.Reset();
    }
    // other parts of IEnumerator<T>, such as Current
}

// somewhere else in the application
// we can now write processing code without concern for sourcing
// and perhaps projecting the data. this example is very simple,
// but using decorator enumerator you can accomplish very complex
// processing of sequences while maintaining small, testable, and
// composable classes. it also allows for highly configurable
// processing, since the decorators become building blocks.
public class DownloadedDataProcessor
{
    private readonly IEnumerator<MyProjectedListItem> enumerator;
    public DownloadedDataProcessor(IEnumerator<MyProjectedListItem> enumerator)
    {
        this.enumerator = enumerator;
    }
    public void ProcessForever()
    {
        while (true)
        {
            while (enumerator.MoveNext())
            {
                Process(enumerator.Current);
            }

            enumerator.Reset();
        }
    }
    private void Process(MyProjectedListItem item)
    {
        // top secret processing
    }
}

mhand
  • 1,231
  • 1
  • 11
  • 21
-1

There a many instances where I need to do this, so what I do is call reset in my GetEnumerator method. Here is an example:

public IEnumerator GetEnumerator()
{
    this.Reset(); // Reset each time you get the Enumerator
    return (IEnumerator)this;
}
  • The accepted answer is much better; you simply shouldn't be having the your `IEnumerable` returning the same instance of `IEnumerator` each time. Using this approach you cannot *ever* have two different iterators iterating the same list. Using a proper implementation having multiple iterators isn't a problem at all. – Servy Oct 14 '13 at 20:07
  • That's a good point. My answer was more direct to the original question which was iterating the collection two separate times. – user2880159 Oct 15 '13 at 13:40