Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

IInterfaces Part 1 � Implementing IEnumerable and IEnumerator.

0.00/5 (No votes)
5 Mar 2005 1  
Part 1 of carefully crafted examples that demonstrate the usefulness of implementing various interfaces.

Output from Example10.cs

Introduction

This is the first of two parts. While part 1 focuses mostly on the IEnumerable and IEnumerator, and part 2 focuses primarily on IComparable and IComparer, the articles are written to seamlessly flow from one to the other.

Although there are a few articles written that explain the use of interfaces, I found myself struggling to understand how to implement them. The distinction between what is necessary and what is auxiliary was often blurred to the point that I was confused about what was needed, and often times I ported code that just didn�t matter in my implementation.

As part of another DirectX project I�m working on, I implemented a class that is an array container of another type of class, that needed to be sortable, that needed to be iteratable with a foreach() construct, and that needed to grow dynamically. While I may not stick with this design, when I�m done (I�m not sure it needs to be dynamic after all), it at long last forced me to crack the code that is interfaces.

Two days later, I rediscovered Arrays UNDOCUMENTED by Wesner Moise. I was brainstorming about an XML parser that I want to write that will probably require a lot of work in MSIL and I thought that reading up on how arrays are implemented might suggest if I�m dreaming an impossible dream. A quarter of the way into the article, Moise implements an array resize function that seemed like a much better design than what I had written.

    public static Array Resize(Array array, int newSize)
    {
        Type type = array.Type;
        Array newArray = Array.CreateInstance(type.GetElementType(), newSize);
        Array.Copy(array, 0, newArray, 0, Math.Min(newArray.Length, newSize))1
        return newArray;
    }

Where my code had required casting of the returned array to make the resize work properly, surely using this code, with its clever use of Array.CreateInstance(), that information would be intrinsic to the design.

Unfortunately, a number of errors in that small chunk of code make it unusable directly. I tracked down the bugs changing array.Type to array.GetType() and the 1 back into a ;. While this new version is probably more efficient than my first draft, I still need to cast the result. I glanced through the comments and was going to mention the bugs I found, but others had already beat me to it� no one mentioned that you need to cast the result. I decided that, I could contribute a small example of the Resize() code in use but I quickly realized that in order to make it useful, I needed to add other Interfaces until parts of it began to resemble the code I had written for my DirectX project. I set out to fix Resize(), and instead found I had the basis of an article on interfaces.

With this project, I tried to provide a clean implementation of various interfaces, while at the same time exploring some of the powerful ways that the object-orientated nature of C# allows you to derive from and extend classes. It is as much an exploration of what I believed C# would allow me to do (and verified), as it is an expose on IEnumerable, IEnumerator, IComparable and IComparer.

Background

The difference between deriving from a class, class myClass : StringDictionary and implementing an interface, class myClass : IEnumerable seems subtle. One of my reference texts, A Programmer�s Introduction to C#, Second Edition by Eric Gunnerson, says that �The choice between class and interface should be fairly straightforward. Classes are only appropriate for �is-a� relationships (where the derived class is really an instance of the base class), and interfaces are appropriate for all others.�

Since StringDictionary implements IEnumerable, the distinction was not clear to me. I implemented a parameter value pair parser and derived it from StringDictionary. I implemented a command-line argument parser and derived it from StringDictionary. With StringDictionary, I didn�t need anything else.

Then I created a one-dimensional array of moderately complex structures. None of the classes I had used in the past, StringDictionary, StringCollection, NameValueCollection, etc., accurately described this mess I had created. If I wanted to use a foreach() construct, then I needed to implement the IEnumerable interface.

Finally the difference was clear to me. The parameter value pair parser and the command-line argument parser were string dictionaries. In fact, a lot of the data structures that I had been working with were easily described that way. Generally, I would have some type of key, and then there would be some sort of value associated with that key � or perhaps no value, but a null value is still a value in this exercise, it�s just an unspecified value. This is why I was able to easily create new classes derived from StringDictionary, and thereby automatically inheriting the richness associated with those classes.

Implementing an interface seems a step backwards by comparison, and in some ways it is. In fact, implementing IEnumerable only has one public method, GetEnumerator, and that�s something that you have to implement. Why would you possibly want to inherit an interface then? According to MSDN, the GetEnumerator method <blink>�Returns an enumerator that can iterate through a collection.�</blink>. Although it won�t actually be blinking for you (does anyone else miss the good ole days of Netscape 1.1n?), that subtle sentence from MSDN summarizes the actual reason we implement interfaces.

Recall that, part of the reason I started using StringDictionary derived classes earlier was the ease in which I could manage those resources. I could add to the dictionary, search for a value based on a key, and/or use a foreach() construct to iterate through the collection. What�s more, StringDictionary derived classes can do that because they too implement IEnumerable. Implementing an interface isn�t so much about inheriting cool methods that make your life easier, it�s about broadcasting that you�ve implemented cool methods that allow other functions and methods in other classes to work with your code to make your life easier.

An interface is a contract. It says that if you implement your class with the public properties and methods that are defined by the interface, then code from somewhere else can work with your code without any prior knowledge or even an understanding about what it does.

Using the code

The layout of these code blocks does not reflect how they are represented in the source code, but rather they are laid in a fashion which follows how I approached each problem.

Example1.cs

At the heart of everything lies the corrected array resize code. As you can see, this implementation is not very different from the previously listed version, but there are a few changes.

using System;

namespace ArrayResizeExample
{
    public class Fruit
    {
        public string Name
        {
            get
            {
                return( "Fruit" );
            }
        }
    }

    public class FruitBasket
    {
        Fruit[] basket = new Fruit[1];
        int count = 0;

        public void Add( Fruit fruit )
        {
            if( count >= basket.Length )
            {
                basket = (Fruit[]) Resize( basket, basket.Length * 2 );
            }
            basket[count++] = fruit;
        }

        public static Array Resize( Array array, int newSize )
        {
            Type type = array.GetType().GetElementType();
            Array newArray = Array.CreateInstance( type, newSize );
            Array.Copy( array, 0, newArray, 0, Math.Min( array.Length, newSize ));
            return newArray;
        }
    }

    class ArrayResizeExample
    {
        /// <summary>

        /// The main entry point for the application.

        /// </summary>

        [STAThread]
        static void Main(string[] args)
        {
            FruitBasket fruitBasket = new FruitBasket();

            fruitBasket.Add( new Fruit() );
            fruitBasket.Add( new Fruit() );
        }
    }
}

While it is perhaps successful in implementing and testing the array resize code without setting breakpoints and stepping through the code, it is difficult to see the fruits of our labor. (Of course the pun is intended, anyone who tells you a pun isn�t intended is lying).

Example2.cs

To make things interesting, I wanted to iterate over the fruit I added to my basket, and to print out the contents.

        static void Main(string[] args)
        {
            FruitBasket fruitBasket = new FruitBasket();

            fruitBasket.Add( new Fruit() );
            fruitBasket.Add( new Fruit() );

            Console.WriteLine( "The basket is holding:" );
            foreach( Fruit fruit in fruitBasket )
            {
                Console.WriteLine( "  a(n) " + fruit.Name );
            }
        }

I changed the Main function to include the code I want to use. If you try to compile this now, you will get an error:

Example2.cs(53): foreach statement cannot operate on variables of type 
'ArrayResizeExample.FruitBasket' because 'ArrayResizeExample.FruitBasket' does not 
contain a definition for 'GetEnumerator', or it is inaccessible.

The foreach() statement is looking for a class or struct that inherits IEnumerable and which implements GetEnumerator(). Add using System.Collections to the top of the file.

using System;
using System.Collections;

Then add IEnumerable to the FruitBasket class.

    public class FruitBasket : IEnumerable

When you do, Visual Studio 2003 (I�m not sure about earlier versions) does something wonderful. It prompts you to press tab

Press TAB to implement stubs for interface 'System.Collections.IEnumerable'

whereupon, it creates a region prepopulated with the methods and fields that you must support.

        #region IEnumerable Members

        public IEnumerator GetEnumerator()
        {
            // TODO: Add FruitBasket.GetEnumerator implementation

            return null;
        }

        #endregion

While it will crash with an exception,

An unhandled exception of type 'System.NullReferenceException' occurred in Example2.exe
Additional information: Object reference not set to an instance of an object.

the code will compile.

Stepping through the code reveals that it is crashing with the foreach call; since we haven�t done anything with our GetEnumerator method yet, this shouldn�t be too surprising.

Example3.cs

Looking at the MSDN documentation in more detail (and even the implemented GetEnumerator function more closely), we find that GetEnumerator is supposed to return an IEnumerator. What this means for us, is that we need a class to implement IEnumerator and the associated methods and properties. This can be done by using a comma to separate the two interfaces in the class definition.

    public class FruitBasket : IEnumerable, IEnumerator

GetEnumerator is also updated to reflect that this class is implementing the IEnumerator members.

        #region IEnumerable Members

        public IEnumerator GetEnumerator()
        {
            return( this );
        }

        #endregion

        #region IEnumerator Members

        public void Reset()
        {
            // TODO:  Add FruitBasket.Reset implementation

        }

        public object Current
        {
            get
            {
                // TODO:  Add FruitBasket.Current getter implementation

                return null;
            }
        }

        public bool MoveNext()
        {
            // TODO:  Add FruitBasket.MoveNext implementation

            return false;
        }

        #endregion

Because we haven�t finished implementing the details, we are far from finished, but this will now compile and won�t crash � it won�t do what it�s supposed to do either, but at least it isn�t crashing now.

Example4.cs

I prefer separating the IEnumerable and IEnumerator implementations out. I think this makes maintenance of the code easier, but it is a personal preference. If you can so code it, the code doesn�t care and you can support several different interfaces in one class. For example4.cs and the rest of this article, I will use a separate class called FruitBasketEnumerator. Return the class FruitBasket to its former itself,

    public class FruitBasket : IEnumerable

create FruitBasketEnumerator,

    public class FruitBasketEnumerator : IEnumerator
    {
        FruitBasket fruitBasket;

        #region IEnumerator Members

        public void Reset()
        {
            // TODO:  Add FruitBasketEnumerator.Reset implementation

        }

        public object Current
        {
            get
            {
                // TODO:  Add FruitBasketEnumerator.Current getter implementation

                return null;
            }
        }

        public bool MoveNext()
        {
            // TODO:  Add FruitBasketEnumerator.MoveNext implementation

            return false;
        }

        #endregion
        internal FruitBasketEnumerator( FruitBasket fruitBasket )
        {
            this.fruitBasket = fruitBasket;
            Reset();
        }
    }

and modify GetEnumerator to reflect this change.

        #region IEnumerable Members

        public IEnumerator GetEnumerator()
        {
            return( new FruitBasketEnumerator( this ));
        }

        #endregion

There is another reason to do it this way. By using a separate FruitBasketEnumerator class, I can also make a constructor that takes care of various details when I�ve instantiated the class, or more accurately when foreach() has. One such detail is resetting the index.

The MSDN documentation for Reset says that it �Sets the enumerator to its initial position, which is before the first element in the collection.� With a 0 based index, the position just before the first element is -1. This means two things for us, first we need to keep track of the index for the life of the class so add a new field,

    public class FruitBasketEnumerator : IEnumerator
    {
        FruitBasket fruitBasket;
        int index;

and then set index to -1 in the Reset method.

        public void Reset()
        {
            index = -1;
            revision = fruitBasket.Revision;
        }

MoveNext, according to MSDN, �Advances the enumerator to the next element of the collection.� It is also important to note, that it returns a bool. �true if the enumerator was successfully advanced to the next element; false if the enumerator has passed the end of the collection.�

Fortunately for us, fruitBasket has already been keeping track of how many Fruit classes have been added in its private field int count. Adding an accessor Count to the FruitBasket class allows us to safely expose that data. The internal keyword restricts access only to files within the same assembly. For our purposes, that is enough exposure.

        internal int Count
        {
            get
            {
                return( count );
            }
        } 

This allows us to implement the MoveNext method of the IEnumerator interface.

        public bool MoveNext()
        {

            if( ++index >= fruitBasket.Count )
                return( false );
            else
                return( true );
        }

At this point, compiling and executing fails with another exception:

An unhandled exception of type 'System.NullReferenceException' 
occurred in Example4.exe.
Additional information: Object reference not set to an instance of an object.

We haven�t yet implemented Current, and as Visual Studio has preauthored it, we are returning a null. This is in fact causing our System.NullReferenceException. Current, according to MSDN is simple enough; it just �gets the current element in the collection.� While we have access to the instance of the fruitBasket class, we haven�t yet exposed a way for our FruitBasketEnumerator to access the Fruit kept in the fruitBasket. In the FruitBasket class, add this accessor. This will let you store and retrieve Fruit from the basket at specific indices.

        internal Fruit this[int index]
        {
            get
            {
                return( basket[index] );
            }
            set
            {
                basket[index] = value;
            }
        }

Then change the Current method to return the Fruit to the foreach() construct.

        public object Current
        {
            get
            {
                return( fruitBasket[index] );
            }
        }

If you compile and run this code, you will see that foreach() now operates as we would have expected it to. We add fruit to the basket, and when we retrieve it, we look at each of those objects we�ve added.

This is still a bit like when we first implemented the array resize code. If we step through the code, we can see it pulling data from the correct structures, but it still difficult to tell when we just run it from the command line. At this point, fruit is just fruit.

Example5.cs

Q: When is fruit more than just fruit? A: When fruit is Apples, Bananas and Cantaloupes. Things are going to get sticky. Change the Fruit class� Name member to a virtual member.

    public class Fruit
    {
        public virtual string Name
        {
            get
            {
                return( "Fruit" );
            }
        }
    }

Add the following derived classes to the namespace, overriding the virtual member.

    public class Apple : Fruit
    {
        public override string Name
        {
            get
            {
                return( "Apple" );
            }
        }
    }

    public class Banana : Fruit
    {
        public override string Name
        {
            get
            {
                return( "Banana" );
            }
        }
    }

    public class Cantaloupe : Fruit
    {
        public override string Name
        {
            get
            {
                return( "Cantaloupe" );
            }
        }
    }

Eric Gunnerson would be proud because an Apple is-a Fruit, a Banana is-a Fruit, and a Cantaloupe is-a Fruit. Derived classes are the perfect choice because we can derive and extend from a base class that might already have members that make our life easier. This also makes maintaining the code easier, as we will see a little later. For now, this is enough to make our code do magic. Change Main so that we are no longer adding just fruit,

        static void Main(string[] args)
        {
            FruitBasket fruitBasket = new FruitBasket();

            Console.WriteLine( "Adding a Banana" );
            fruitBasket.Add( new Banana() );
            Console.WriteLine( "Adding an Apple" );
            fruitBasket.Add( new Apple() );
            Console.WriteLine( "Adding Fruit" );
            fruitBasket.Add( new Fruit() );

            Console.WriteLine( "" );

            Console.WriteLine( "The basket is holding:" );
            foreach( Fruit fruit in fruitBasket )
            {
                Console.WriteLine( "  a(n) " + fruit.Name );
            }
        }

Compile and run. Not only does our fruitBasket now keep different kinds of fruit, but we can also recall the names of the fruit we added. Still, we can do more as documented in part 2.

Summary

In part 1, the example code demonstrates the need for, and the process of, implementing IEnumerable and IEnumerator interfaces. Please continue with part 2 where we will add IComparable and IComparer interfaces to the project to introduce additional capabilities.

History

  • 5 March, 2005

    Original article.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here