By Richard Hernandez
We are going to use "foreach" to iterate through our collection of fruit objects.
Lets assume that we have a class called Fruit which contains the fields; string name ,int num and bool ripe. We build a class called Fruits which represents a collection of fruit objects.
public class Fruits
{
//we don’t have to specify private as this is the default modifier
//it’s just included to highlight the fact that this is private to
//the Fruits collection.
private Fruit[] fruitArray;
//Create a basket of fruit
public Fruits()
{
fruitArray = new Fruit[3];
fruitArray[0] = new Fruit("Banana",false,3);
fruitArray[1] = new Fruit("Apples",true,5);
fruitArray[2] = new Fruit("Oranges",true,3);
}
}
In order to obtain each Fruit from the Fruits collection it would be convenient if the object user could iterate over the Fruits type using the foreach construct. It would be reasonable to code:
public class FruitBar
{
public static void Main()
{
Fruits fruitbasket = new Fruits();
//Show only the ripe fruit
foreach (Fruit f in fruitbasket)
{
if (f.ripe == true)
{
Console.WriteLine("Name : {0}",f.name);
Console.WriteLine("Amount : {0}",f.num);
}
{
}
}
Unfortunately attempting to execute this code results in the compiler complaining that it cannot find GetEnumerator.
To use the foreach syntax your class must support the IEnumerable interface found in the System.Collections namespace. IEnumerable defines only one method, public IEnumerator GetEnumerator(), notice that this method returns another interface IEnumerator also found in System.Collections namespace.
IEnumerator supports a simple iteration over a collection. It publishes two methods and a property:
bool MoveNext():
Advances the enumerator to the next element in the collection. Returns true if successfully advanced to the next element in the collection and false if the move would take it beyond the last element.
void Reset():
Sets the enumerator to the initial position, which is before the first element in the collection (-1)
object Current {get;}:
Gets the current element in the collection. Throws Exception Type InvalidOperationException.
To successfully implement an enumerator we must keep in mind the following protocol. After an enumerator is created or after a Reset, MoveNext must be called to advance the enumerator to the first element of the collection before reading the value of Current. Current throws an exception if the last call to MoveNext returns false. Current does not move the position of the enumerator.
This is by no means a thorough discussion on implementing enumeration, more an introduction to using enumerators with foreach.
Here is an example of the changes required to implement an enumerator for use with the foreach construct.
namespace MyFruitBasket
{
public class Fruit
{
//this is a bad way to declare fields in a class
//but this is just an example
public string name;
public bool ripe;
public int num;
public Fruit(string nme, bool rp, int n)
{
name = nme;
ripe = rp;
num = n;
}
}
The Fruits collection implements the IEnumerable and IEnumerator interfaces.
public class Fruits :IEnumerable,IEnumerator
{
Fruit[] fruitArray;
//current position in array set to initial position
int position = -1;
//Create a basket of fruit
public Fruits()
{
fruitArray = new Fruit[3];
fruitArray[0] = new Fruit("Banana",false,3);
fruitArray[1] = new Fruit("Apples",true,5);
fruitArray[2] = new Fruit("Oranges",true,3);
}
//Implement IEnumerable
public IEnumerator GetEnumerator()
{
return (IEnumerator)this;
}
//Implement IEnumerator
public bool MoveNext()
{
position++;
if (position < fruitArray.Length)
{
return true;
}
else
{
return false;
}
}
public void Reset()
{
position = -1;
}
public object Current
{
get{return fruitArray[position];}
}
}
class FruitBar
{
static void Main(string[] args)
{
Fruits fruitbasket = new Fruits();
//Present only the ripe fruit
foreach (Fruit f in fruitbasket)
{
if (f.ripe == true)
Console.WriteLine("Name : {0}",f.name);
Console.WriteLine("Amount : {0}",f.num);
}
string x = Console.ReadLine();
}
}
}
I hope this has been of some help.