ARTICLE

Support Foreach for your Collection class

Posted by Sivaraman Dhamodaran Articles | C# Language November 04, 2010
This article walks you through how to enable foreach loop construct for your custom collection class.
Reader Level:
Download Files:
 

What is ForEach?

 

"ForEach" is a loop like "For", "While" etc. When you are using foreach loop you do not need to worry about where the collection starts and where it ends. These are taken care of by the foreach loop itself. You might have already used it when you are working with array. If not, below is a simple example:

 

int[] numbers = new int[4];

numbers[0] = 1;

numbers[1] = 2;

numbers[2] = 3;

numbers[3] = 4;

 

foreach(int num in numbers)

            Console.WriteLine(num);

 

The foreach loop above will iterate through all the 4 numbers allocated on the array and prints that in the console window. Note that we do not worry about start and end as it is taken care of by the foreach. Now, let us move on to how can we implement such a foreach for our collection class.

 

The Goal

 

Let us implement the foreach loop construct with the Order Class. The Order class has a collection of Products that participate in the order identified by the product id. Our goal is to provide support for foreach on the Order class so that the Client (User of your class) can iterate through the Products.

 

IEnumerable and IEnumerator

 

Both interfaces are exposed by the .Net framework. If you want to support "foreach" then your collection participants should implement these contracts. IEnumerable tells that your class's internal collection (has-a with array, list etc) can be Enumarable by the class, which implement IEnumerator. It might be a little confusing now; come back here and read this paragraph once again when you have finished reading this write-up.

 

The Product Class

 

The class is self-explanatory and hence I am leaving it for you to understand yourself.

//001: Product Class.

public class Product

{

            private int ProductId;

            private string ProductName;

 

            public Product(int id, string Name)

            {

                        ProductId = id;

                        ProductName = Name;

            }

 

            public override string ToString()

            {

                        return ProductName;

            }

}

 

 

The Order class

 

This is our collection class. From client (calling code) point of view the Order has Collection of Products. If I have an order object and I do have list of products placed on that order. We are going to support foreach construct for this order class. So this Order class should implement the IEnumerable. IEnumerable contract expects to implement a function called: GetEnumerator and an object that implements IEnumerator. Also it expects that create a fresh instance of IEnumerator implemented object and return that back to the caller.

 

Let us start implementing the Order class. First, as already told Order is collection of Products. So an array of product is declared. The index is just for iterating through the Products collection, which we will see later here.

 

//002: Order Class is Enumerable to support ForEach loop

public class Order:IEnumerable

{

            private Product[] products;

            private int OrderID;

            private int index = 0;

 

Constructor of the order class takes order id and variable number of parameter of type Product. Note down the usage of the keyword params, which allows the client to pass any number product instance. Next these variable numbers of arguments (treated as array inside the constructor/function) are copied to the member of the Order class. Note the usage of foreach when we do the copy. This is working as array implemented it & we are going to the same for our custom class Order. Below is the constructor:

 

//003: Constructor for Order Class. Initialize the Products

public Order(int ordid, params Product[] col_of_products)

{

            products = new Product[ col_of_products.Length ];

            //003_1: The built-in array supports foreach. Our aim is to provide the ability of this

            //                                  of Foreach loop for Order Class

            foreach (Product prod in col_of_products)

            {

                        products[index] = prod;

                        index++;

            }

 

            //003_2: Initialize order. However not required for this example

            OrderID = ordid;

}

 

 

As the order class agreed to the contract IEnumerable supplied by the .Net framework, we need to implement the GetEnumerator function. This function should return an object that implements (agreed to the .Net built-in contract IEnumerator) the IEnumerator interface. Note that always create instance and return back. Below is the implementation of the contract function:

 

//004: Implement the contract expected by IEnumerable.

//                                Note: Always return a new Instance. Do not confuse with Reset.

//                                It will be invoked in COM Interops as Per MSDN

public IEnumerator GetEnumerator()

{

            return new OrderIterator(this);

}

 

Now we will go ahead and implement the class OrderIterator. Yes, your guess is correct, we will implement IEnumerator. Let us move to OrderIterator Class. I am going to keep this class as inner class as it is absolutely not necessary for the client to know what kind implementation it has. But, still you can go ahead and keep it as a separate class.

 

Inner class is worth a separate article. But as we are going to use here, below is the short note.

1)     Inner class is part of some other class, and it is defined inside containing class. In our example the containing class is Order.

2)     Outer class can access inner class members after creating an instance of it.

3)     It is not possible to create the Instance of outer class inside the inner class.

4)     The only way inner class to access the outer class member is either through inheritance or through by having a reference to it (Passed as parameter).

5)     The client can know about outer class, but inner class implementation is hidden.

 

 

The OrderIterator inner class

 

This inner class implements the IEnumerator interface. To fulfill the contract MoveNext(),Reset()function and Current property needs to be implemented. However, the Reset() function is useful in the COM interop, as it is part of the contract an implementation required.

 

The Order class variable is declared in this class (Do not confuse with point(3), look at point(4)) to access the Products collection. The variable itr_index is used to refer the each element in the Products array. Below is the class with the data members:

 

//005: Inner Class for the Iterator (Enumerator). 

private class OrderIterator: IEnumerator

{

            private Order order;

            private int itr_index = -1;

 

The constructor takes object of Order class as parameter and stores it. Note that we are not creating the Order object here, we are just referring the already created object passed in. The constructor is shown below:

 

//005_1: Constructor for the Enumerator. Have a reference to the IEnumarable class that requires an Enumerator.

public OrderIterator(Order order)

{

            this.order = order;

}

 

In the contract function MoveNext, we will move our index towards the left of the Products collection. That is; just increment the itr_index and after check and return the location is valid or not. Below is the contract function:

 

//005_2: Contract Expected by IEnumerator. As return type is bool, it is our responsibility to say 'You can not move

//        next further'.

public bool MoveNext()

{

            //Move to the First Location.

            itr_index++;

            if ( itr_index < order.products.Length )

                        return true;

            else

                        return false;

}

 

 

And, the Reset function reset the itr_index. Simple.

 

//005_3: Contract Expected by the IEnumerator. Reset. No explanation required. Dot

public void Reset()

{

            itr_index = -1; //Let the Movenext start from the beginning

}

 

And, the Reset function reset the itr_index. Simple.

 

The last contract that we need to finish-off is implementing the property Current. Note that the property is expected as read-only by the IEnumerator. As our itr_index is in the right position (Because the framework calls the MoveNext before accessing Property Current) the implementation just returns the Product by picking it from Products collection using the itr_index. Below is the implementation for property Current:

//005_4: Contract expected by IEnumerator. Implement the read-only property

public object Current

{

            get

            {

                        return order.products[itr_index];

            }

}

 


What we have done so for

 

1)    We created a class Order that maintains a collection of Products. As the Order Collection class implements the IEnumarable it allows iteration of Products possible.

2)    The Product class is the single entity that will be collected in the Order class [To have a meaningful order]

3)    We created an inner class for Order, which does the processing of iteration on the Products collection. Of course it implements the IEnumerator.

 

That's all we are done. Take a break and go for coffee before we go on testing the foreach loop construct for our Order Collection class.

 

The client Code

 

The client code first creates six products and then places them in the Order class. Then client uses the foreach to iterate through all the Product in a particular order. Below is Client Code:

 

//Client 001: Create the Products

Product p1 = new Product(1001, "Beer");

Product p2 = new Product(1002, "Soda");

Product p3 = new Product(1003, "Tea");

Product p4 = new Product(1004, "Coffee");

Product p5 = new Product(1005, "Apple");

Product p6 = new Product(1006, "Grapes");

 

//Client 002: Create the Order Class, that has the collection product that is order placed for these products.

//                                                        Look at the Constructor. The Params stands for variable number of arguments collected as

//                                                        array

Order new_order = new Order(1001, p1, p2, p3, p4, p5, p6 );

 

//Client 003: Let us go to the Last step of Testing the ForEach Loops.

foreach(Product ordered_item in new_order)

{

            if (ordered_item != null )

                        Console.WriteLine("Product in Order: " + ordered_item);

}

 


How the foreach works for our collection class

 

Refer the below picture:

Pic1.JPG

 

1)    The execution first reach at new_order. At this we know that new_order is object of Order class. And order class implemented IEnumerable. So, first we will get the Object that Implemented the IEnumerator using IEnumerable's GetEnumerator() function.

2)    Next we know it is foreach and a call to MoveNext takes place.

3)    Next, it is time for "ordered_item in" Current property is accessed and we get the Single entity that is; Product in ordered_item.

 

The numbered notation is shown below to explain the flow in the foreach:

1=>2=>3=>2=>3=>2 until all the items are iterated

 

Number is the Picture represents the following:

Number 1: Execution reaches to Collection class Instance. That is; Order

Number 2: Execution reads the foreach keyword and makes a call to MoveNext

Number 3: Execution reaches Product order_item in and make a call to Current property, pick-up the value, assigns it to ordered_item.

 

That's all. See you all in the Next article.

COMMENT USING