CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Results 1 to 9 of 9
  1. #1
    Join Date
    Jun 2005
    Posts
    66

    Multiple Enumerators

    Hi,

    is it possible to have more than one enumerator in one class? I'd like to achieve something like:

    Code:
    foreach( string main in myenumclass )
      foreach( int sub in myenumclass.dummy[main] )
        MessageBox.Show( main + ": " + sub.ToString( ) );
    I managed to setup the first enumerator (outer loop). With "main" a sub array shall be accessed. However this array is no real array. Its items have to be taken from a dataset. I could return the complete row, but that's not what I want. I'd like to pack the row items in a separate object which will be returned (in my example it's just a int for simplicity).

    Hope you understand my problem.
    Any help would be appreciated.

    gbr
    Last edited by gbr; June 10th, 2005 at 04:21 AM.

  2. #2
    Join Date
    Apr 2005
    Posts
    576

    Re: Multiple Enumerators

    As far as I can figure out, you can have multiple iterators for a class, but IEnumerable can only return one of them.

    If you want to iterate with foreach, then you must somehow switch state of the object you are iterating over, so that IEnumerable.GetEnumerator returns the desired iterator. But it will not be a very intuitive solution, so I would not really recommend that.

    You could also iterate with just a while statement, you can have several methods that each return a different enumerator.

    But, I have never done this and I can't really figure out when this is needed.

    In your example, you are iterating over two different objects, if just myenumclass.dummy[main] returns some other object of a different class than myenumclass, and you implement IEnumerable in that class, then you are done.

  3. #3
    Join Date
    Mar 2005
    Location
    West Michigan
    Posts
    20

    Re: Multiple Enumerators

    I had a similar situation this week where I had nested lists of XML nodes, and I wanted to iterate over them all. Say you have a class with nested data:
    Code:
    public class Nestor
    {
      public class Sub
      {
        public ArrayList inner;
      }
      public ArrayList outer;
    }
    And you want to do this:
    Code:
    foreach (Object inner in nestor.outer)
    {
      // Do somthing to each inner object
    }
    Here's what I did:
    Code:
    public class Nestor : IEnumerable
    {
      public class Sub
      {
        public ArrayList inner;
      }
      public ArrayList outer;
    
      public class NestedEnumerator : IEnumerator
      {
        private ArrayList mOuter;
        private IEnumerator mEnumOuter;
        private IEnumerator mEnumInner;
    
        public NestedEnumerator(ArrayList outer)
        {
          mOuter = outer;
          Reset();
        }
    
        public void Reset()
        {
          mEnumOuter = mOuter.GetEnumerator();
          mEnumOuter.MoveNext();
          mEnumInner = mEnumOuter.Current.GetEnumerator();
        }
    
        public object Current()
        {
          return mEnumInner.Current;
        }
    
        public bool MoveNext()
        {
          if (!mInner.MoveNext())
          {
            if (mOuter.MoveNext())
            {
              mInner = mOuter.Current.GetEnumerator();
              return true;
            }
            else
            {
              return false;
            }
          }
          return true;
        }
      }
    
      public IEnumerator GetEnumerator()
      {
        return new NestedEnumerator(this);
      }
    }
    This just uses simple arrays for the collections, but you can use anything you want by modifying the Reset, Current, and MoveNext methods to iterate over what ever you need. Checks for empty lists also needs to be added.

    If there is a more simple way to do this, I'd be interested in seeing it.
    --
    Scott

  4. #4
    Join Date
    Apr 2005
    Posts
    576

    Re: Multiple Enumerators

    Quote Originally Posted by wsmmi6674
    I had a similar situation this week where I had nested lists of XML nodes, and I wanted to iterate over them all. Say you have a class with nested data:
    Code:
    public class Nestor
    {
      public class Sub
      {
        public ArrayList inner;
      }
      public ArrayList outer;
    }
    And you want to do this:
    Code:
    foreach (Object inner in nestor.outer)
    {
      // Do somthing to each inner object
    }
    ...
    Sorry, your example doesn't make sense to me, since there is no visible relation between the classes Nestor and Nestor.Sub (other than Sub being an inner class of Nestor). And the code solution you supplied won't compile.

    If the outer ArrayList contains Nestor.Sub objects, won't a double loop be enough:

    Code:
    foreach (Object o in nestor.outer)
    {
       Nestor.Sub sub=(Nestor.Sub)o;
       foreach (Object innerObject in sub.inner)
       {
          // Do somthing to each inner object
       }
    }
    In a more elaborated example, nestor and sub could each implement their own enumerators (or inherit from CollectionBase).

  5. #5
    Join Date
    Dec 2003
    Location
    Republic of Ireland
    Posts
    383

    Re: Multiple Enumerators

    Quote Originally Posted by klintan
    If you want to iterate with foreach, then you must somehow switch state of the object you are iterating over, so that IEnumerable.GetEnumerator returns the desired iterator.
    Have you have any idea how it could be done? The best will be to determine which enumerator should be returned inside GetEnumerator method.

    Any ideas will be welcome,
    thanx in advance.
    Last edited by Mr. Tomaszek; May 10th, 2006 at 04:25 AM.

  6. #6
    Join Date
    Apr 2005
    Posts
    576

    Re: Multiple Enumerators

    Something like this:

    Code:
    public enum EnumeratorType
    {
         EnumerateForward,
         EnumerateBackward,
         EnumerateByName
    }
    private EnumeratorType _enumerator;
    public void SetEnumerator(EnumeratorType enumerator)
    {
        _enumerator=enumerator;
    }
    public IEnumerator GetEnumerator()
    {
         switch (_enumerator)
         {
              case EnumerateForward:
                   return new ForwardEnumerator(this);
              case EnumerateBackward:
                   return new BackwardEnumerator(this);
              case EnumerateByName:
                   return new ByNameEnumerator(this);
         }
    }
    private class ForwardEnumerator: IEnumerator
    {
        ...
    }
    private class BackwardEnumerator: IEnumerator
    {
        ...
    }
    private class ByNameEnumerator: IEnumerator
    {
        ...
    }
    I have not tried it though. What I have done is that I have used inner classes for a collection, where each inner class represents a subset of the collection and has its own iterator.

  7. #7
    Join Date
    Dec 2003
    Location
    Republic of Ireland
    Posts
    383

    Re: Multiple Enumerators

    Thanx for quick reply.
    I did that in exactly the same way as you have presented. However this solution has one serious drawback. As you mentioned in your earlier post, you have to manually switch to use given enumerator. What I want to do is to allow some kind of automatic switching. Alas, there is no evidence inside GetEnumerator method, what enumerator is intended to be used.

  8. #8
    Join Date
    Apr 2005
    Posts
    576

    Re: Multiple Enumerators

    I agree with the drawback. As I mentioned before, what I have done for one class where I needed different iterations, was to add an inner class that supported the required iterations, like this:

    Code:
    public class TestCollection: CollectionBase
    {
    
    	private BackwardCollection _backCol;
    	public class BackwardCollection: IEnumerable
    	{
    		private TestCollection _col;
    		internal BackwardCollection(TestCollection col)
    		{
    			_col=col;
    		}
    		public IEnumerator GetEnumerator()
    		{
    			return new BackEnumerator(_col);
    		}
    
    		private class BackEnumerator: IEnumerator
    		{
    			private int _currIndex;
    			private TestCollection _col;
    			public BackEnumerator(TestCollection col)
    			{
    				_col=col;
    				_currIndex=_col.Count;
    			}
    
    			public object Current
    			{
    				get
    				{
    					if (_currIndex == -1 || _currIndex == _col.Count)
    						throw new InvalidOperationException();
    					return _col[_currIndex];
    				}
    			}
    
    			public bool MoveNext()
    			{
    				--_currIndex;
    				return (_currIndex>=0);
    			}
    
    			public void Reset()
    			{
    				_currIndex= _col.Count;
    			}
    
    		}
    
    	}
    
    	public TestCollection()
    	{
    		_backCol=new BackwardCollection(this);
    	}
    
    	public BackwardCollection Backwards
    	{
    		get { return _backCol; }
    	}
    	public void Add(object o)
    	{
    		List.Add(o);
    	}
    
    	public object this[int index]
    	{
    		get { return List[index]; }
    	}
    }
    Call it like this:

    Code:
    TestCollection tc=new TestCollection();
    tc.Add("One");
    tc.Add(2);
    tc.Add("Three");
    
    foreach (object o in tc)
    	Console.WriteLine(o);
    foreach (object o in tc.Backwards)
    	Console.WriteLine(o);

  9. #9
    Join Date
    Dec 2003
    Location
    Republic of Ireland
    Posts
    383

    Re: Multiple Enumerators

    Hi,
    I've lerned another way to do it. It's working only in .NET 2.0
    Just take a look how cool it is

    PS.
    It have some similarities to your's solution.

    Code:
    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Collections;
    
    namespace numtienumerator
    {
        class Program
        {
            public class MultiEnumerator
            {
                private string[] Names = { "Ala", "Ola", "Ela", "Moszunsi", "Goszunsi" };
                private int[] Numbers = { 1, 23, 55, 75, 104 };
    
                public IEnumerable NamesCollection()
                {
                    for (int i = 0; i < Names.Length; i++)
                    {
                        yield return Names[i];
                    }
                }
    
                public IEnumerable NumbersCollection()
                {
                    for (int i = 0; i < Numbers.Length; i++)
                    {
                        yield return Numbers[i];
                    }
                }
    
            }
            static void Main(string[] args)
            {
                MultiEnumerator me = new MultiEnumerator();
    
                foreach (string s in me.NamesCollection())
                {
                    Console.WriteLine(s);
                }
    
                foreach (int i in me.NumbersCollection())
                {
                    Console.WriteLine(i.ToString());
                }
            }
        }
    }

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  





Click Here to Expand Forum to Full Width

Featured