Thursday, November 18, 2010

[C#] - Support Foreach for your Collection Class

What is ForEach in C#?


“ForEach” is a loop like “For”, “While” etc. When you are using the foreach loop you no need to worry about where the collection starts and where it ends. These are taken care by the foreach loop itself. You may already use it when you are working with an 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 by the foreach. Now, let us move how can we implement such a foreach for our collection class.

The Goal


Let us implement the foreach loop construct the Order Class. The Order class has the 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 Client (User of your class) can iterate through the Products.

IEnumerable and IEnumerator


Both are interfaces are exposed by the dot net framework. If you want to support foreachthen your collection participants should implement these contracts. IEnumerable tells that your class’s internal collection (has-a with array,list etc) can be Enumerable by the class, which implement IEnumerator. It may be little confused now, come here and read this paragraph once again when you 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 do have a list of products placed in 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 the 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;

The constructor of the order class takes order id and a 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 an 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 dot.net framework, we need to implement the GetEnumerator function. This function should return an object that implements (Agreed to the Dot.net built-in contract IEnumerator) the IEnumerator interface. Note that always create an 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 an 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.

The inner class is worth a separate article. But as we are going to use here, below is the short note.
1)     The 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 the 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 a parameter).
5)     The client can know about outer class, but the 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 and 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 an 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 have we 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:

1)    The execution first reaches new_order. At this, we know that new_order is the 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 makes a call to Current property, pick-up the value, assigns it to ordered_item.

Source Code: Download

No comments:

Post a Comment

Leave your comment(s) here.

Like this site? Tell it to your Firend :)