Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# IEnumerable, IEnumerator Reset Function Not Get Called

I'm basicly trying to make my class able to iterate using foreach. I read this tutorial. MSDN. It seems very straight forward. However, I have a problem when I want to iterate second time. I debugged it; and it turned out that it doesn't call the Reset() function.

Class A

class A : IEnumerable, IEnumerator
{
    int[] data = { 0, 1, 2, 3, 4 };

    int position = -1;

    public object Current
    {
        get
        {
            return data[position];
        }
    }

    public bool MoveNext()
    {
        position++;
        return (position < data.Length);
    }

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

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

When I run the following main function; it never calls Reset() function. So, after one loop I never be able to iterate my class again.

Main

static void Main(string[] args)
{
    A a = new A();

    foreach (var item in a)
    {
        Console.WriteLine(item);
    }

    Console.WriteLine("--- First foreach finished. ---");

    foreach (var item in a)
    {
        Console.WriteLine(item);
    }
}

Output:

0
1
2
3
4
--- First foreach finished. ---
Press any key to continue . . .

Any thoughts?

like image 869
Sait Avatar asked Jul 13 '12 17:07

Sait


1 Answers

Each time foreach is called, it asks for a new IEnumerator. Returning your class instance is a bad idea - you should make a separate class to implement the IEnumerator, and return it instead.

This is often done by using a nested (private) class, and returning an instance of it. You can pass the class A instance to the private class (giving it access to data), and put the position field in that class. It would allow more than one enumerator to be created simulatenously, and will work properly with subsequent foreach calls.

For example, to modify your code, you'd do something like:

using System;
using System.Collections;

class A : IEnumerable
{
    int[] data = { 0, 1, 2, 3, 4 };

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

    private class AEnumerator : IEnumerator
    {
        public AEnumerator(A inst)
        {
            this.instance = inst;
        }

        private A instance;
        private int position = -1;

        public object Current
        {
            get
            {
                return instance.data[position];
            }
        }

        public bool MoveNext()
        {
            position++;
            return (position < instance.data.Length);
        }

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

    }
}

Note that you can also just return the array's enumerator directly (though I was assuming you were trying to learn how to make clean enumerators):

class A : IEnumerable
{
    int[] data = { 0, 1, 2, 3, 4 };

    public IEnumerator GetEnumerator()
    {
        return data.GetEnumerator();
    }
}

Finally, you can use iterators to implement this in a far simpler manner:

class A : IEnumerable
{
    int[] data = { 0, 1, 2, 3, 4 };

    public IEnumerator GetEnumerator()
    {
        for (int i=0;i<data.Length;++i)
           yield return data[i];
    }
}

That being said, I would strongly recommend implementing IEnumerable<int> in addition to IEnumerable. Generics make this far nicer in terms of usage.

like image 92
Reed Copsey Avatar answered Sep 30 '22 23:09

Reed Copsey