Monday, May 7, 2012

C# Iterations: IEnumerator, IEnumerable and Yield

Directly using IEnumerator for iterations

Enumerators are used to read data in the collection. The foreach statement hides the complexity of the enumerators, but you can directly manipulate IEnumerator for customized iterations. Let's do an example:

Code:
List<string> myList = new List<string>();for (int i = 0; i < 10; i++)
{
    myList.Add("Item " + i.ToString());
}
IEnumerator<string> myEnum = myList.GetEnumerator();

myEnum.Reset();           
myEnum.MoveNext();
myEnum.MoveNext();
myEnum.MoveNext();
System.Console.WriteLine(myEnum.Current);
myEnum.MoveNext();
myEnum.MoveNext();
System.Console.WriteLine(myEnum.Current);
myEnum.Reset();
myEnum.MoveNext();
System.Console.WriteLine(myEnum.Current);

Output:
Item 2
Item 4
Item 0

In order to reach the first element, you should run MoveNext method of Enumerator. Initial Position of Enumerator does not point the first element.

Implementing IEnumerable and IEnumerator on your custom objects

IEnumerable interface should be implemented in order to use your custom objects in the form of a collection (series of values or objects). Which means you can use it directly with the foreach statement. IEnumerable interface has a method called GetEnumerator() which returns an object implemented IEnumerator. Let's do an example: PowersOfTwo class implements IEnumerable so any instance of this class can be accessed as a collection.
class PowersOfTwo : IEnumerable<int>
{      
    public IEnumerator<int> GetEnumerator()
    {
        return new PowersOfTwoEnumerator();
    }       
    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }       
}
Test:
PowersOfTwo p2 = new PowersOfTwo();foreach (int p in p2)
{
    System.Console.WriteLine(p);
}


Output:

2 4 8 16 32 64 128 256 512 1024


Actually the magic trick lies in the PowersOfTwoEnumerator Class
    class PowersOfTwoEnumerator : IEnumerator<int>
    {
        private int index = 0;
        public int Current
        {
            get { return (int)System.Math.Pow(2, index); }
        }

        object System.Collections.IEnumerator.Current
        {
            get { return Current; }
        }
        public bool MoveNext()
        {
            index++;
            if (index > 10)
                return false;
            else                return true;
        }
        public void Reset()
        {
            index = 0;
        }
        public void Dispose()
        {
        }
    }


Current returns the same element until MoveNext is called. Initial index is zero each MoveNext method incriments the index by 1 up to 10 then it returns false. When the enumerator is at this position, subsequent calls to MoveNext also return false. If the last call to MoveNext returned false, Current is undefined. You cannot set Current to the first element of the collection again; you must create a new enumerator instance instead. IEnumerator inherits IDisposible for performance only.

Using The Yield Keyword

yield keyword is introduced by C# 2.0 in order to implement iteartion easier. On the other hand it has some disadventages. Let's first look at the yield keyword.
public IEnumerable<int> GetPowersofTwo()
{
   for (int i = 1; i < 10; i++)       yield return (int)System.Math.Pow(2, i);   yield break;
}


Yield is not a feature of the .Net runtime. It is just a C# language feature. During compilation Compiler converts yield method and its class to instances of IEnumerable and IEnumerator implemented classes.

Criticism of the keyword "yield"

"yield" has a couple of drawbacks first of all it is designed to simplify implementing iterations. But it can be used to write very ill designed code (like goto). There are many bad examples therefore I am not going to write one here. But using it for other than the intended purpose will make your code hard to follow.

Second problem is that "yield" does not make sense in Object Oriented paradigm just like delegates. I believe as long as languages stick to a single paradigm they become more understandable and structured. When Microsoft introduced C# they decided to use delegates as an event mechanism, instead of interfaces. Delegates are function pointers and have no meaning in OOP. A similar problem exists for yield too, when you look at the above example, the method signature tells you that GetPowersofTwo will return an object implemented IEnumerable. But it is not the case.

Example 2 


In the .NET Framework, there are two interfaces designed to allow you to iterate easily over collections of objects as you would typically do in a for loop. Many classes in the .NET Framework have implemented these interfaces and do their work behind the scenes so you don’t have to worry about how it is done. However, sometimes you want to have the control to do this yourself.
Maybe you have the need to create a custom class that holds a collection of objects, and you want to be able to iterate over them. It’s pretty straightforward to do this in C# or VB.NET. You have to implement two interfaces called IEnumerable and IEnumerator. Below is a trivial example (because it’s already possible to do this with the List class) that illustrates how you would go about implementing these to iterate over a collection of string objects:

using System;
using System.Collections.Generic;
using System.Collections;
 
namespace Demo
{
 public class TestOverride : IEnumerable<string>
 {
                private List<string> _values;
 
                public TestOverride(List<string> values)
                {
                    _values = values;
                }
 
  public IEnumerator<string> GetEnumerator()
  {
      return new TestOverrideEnumerator(_values);
  }
 
  IEnumerator IEnumerable.GetEnumerator()
  {
   return GetEnumerator();
  }
 
  protected class TestOverrideEnumerator : IEnumerator<string>
  {
   private List<string> _values;
   private int _currentIndex;
 
   public TestOverrideEnumerator(List<string> values)
   {
    _values = new List<string>(values); 
    Reset();
   }
 
   public string Current
   {
    get { return _values[_currentIndex]; }
   }
 
   public void Dispose() {}
 
   object IEnumerator.Current
   {
    get { return Current; }
   }
 
   public bool MoveNext()
   {
    _currentIndex++;
    return _currentIndex < _values.Count;
   }
 
   public void Reset()
   {
    _currentIndex = -1;
   }
  }
 }
}
I’m sure there’s a better way to do this, but it should help you get started. Note that you have to implement both classes and that the most work is done in the class that implements IEnumerator. Also note that you can use generics (in this case force it to iterate only over string objects).
You would invoke this functionality this way:

List<string> collection = new List<string>();
collection.Add("abc");
collection.Add("def");
collection.Add("ghi");
 
TestOverride collectionWrapper = new TestOverride(collection);
 
foreach (string x in collectionWrapper)
    Console.Out.WriteLine(x);
 
 
 
 

No comments:

Post a Comment