Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to realize right IEnumerator interface for multiple foreach?

I have a code like:

        class T : IEnumerable, IEnumerator
        {
            private int position = -1;

            public T() { }

            public IEnumerator GetEnumerator() { return this; }

            public object Current { get { return position; } }

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

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

//Using in code:
T t = new T();
foreach (int i in t)
 //to do something

In the code above all is working fine but when I use next:

foreach (int i in t)
   if (i == 2)
     foreach (int p in t)
       //print p
   else
       //print i

It prints (in brackets second loop): 0 1 (3 4) 2 instead of 0 1 (0 1 2 3 4) 2 3 4 I tested It on List and Collection and they do It right. How can I to achive what I need?

like image 684
Vasya Avatar asked Dec 09 '22 05:12

Vasya


2 Answers

You can't because you have made your code surface a single enumerator, itself a mistake IMO. A better version would be, for me:

class T : IEnumerable<int> {
    public IEnumerator<int> GetEnumerator() {
        int i = 0;
        while(i < 5) {
            yield return i;
            i++;
        }
    }
    IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
}

The compiler will create the right devices to achieve this with separate enumerators.

Unless you are writing for .NET 1.1, then if you find yourself manually writing an enumarator, there's a very good chance that you are doing it the hard way, and getting it wrong as a bonus.

If you really must do it the hard way:

class T : IEnumerable<int>
{
    public T() { }

    public IEnumerator<int> GetEnumerator() { return new TEnumerator(); }
    IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
    private class TEnumerator : IEnumerator<int>
    {
        private int position = -1;
        public int Current { get { return position; } }
        object IEnumerator.Current { get { return Current; } }
        void IDisposable.Dispose() {}
        public bool MoveNext()
        {
            position++;
            return (position < 5);
        }
        public void Reset() { position = -1; }
    } 
}

The significance here is that different instances of TEnumerator allow the same T instance to be iterated separately.

like image 180
Marc Gravell Avatar answered May 16 '23 05:05

Marc Gravell


foreach (int i in t)
   if (i == 2)
     foreach (int p in t)
       //print p
   else
       //print i

First always use braces, while you indenting matches what will happen another if in there will confuse things.

foreach (int i in t) {
   if (i == 2) {
     foreach (int p in t) {
       //print p
      }
   } else {
       //print i
   }
 }

But you problem: you only have one counter per instance of T, and you are using the same instance. Therefore you do through once. If you want to allow concurrent enumerations the enumerator object will need to be separate with GetEnumerator returning a new instance each time.

like image 44
Richard Avatar answered May 16 '23 05:05

Richard