Tested with Microsoft Visual Studio 2005 8.0.50727.42; .NET Framework 2.0.50727
Exploring the Non-Generic Collection Interfaces
IEnumerable: This is a base System.Collection namespace interface. It enables foreach iterations over an
underlying collection of objects. To be able to use foreach against a collection in a class or struct, that class or
struct must implement the IEnumerable interface. The IEnumerable interface requires that you implement just one
method: GetEnumerator(). GetEnumerator() returns an object that implements the IEnumerator interface (discussed below).
public partial class MyClass
{
public MyClass()
{
TestCollection test = new TestCollection();
// We can use foreach because TestCollection implements IEnumerable.
// Actually, any class that contains a GetEnumerator() method that
// returns an IEnumerator object can be used.
foreach (Object obj in test)
{
}
}
}
public class TestCollection : IEnumerable
{
// I'm using another collection, but we can use our own.
ArrayList al = new ArrayList();
public IEnumerator GetEnumerator()
{
return this.al.GetEnumerator();
}
}
Note that if you want to use a for iteration, you have to implement the IEnumerator interface directly.
IEnumerator: This is a base System.Collection namespace interface. All non-generic enumerators are derived
from the IEnumerator interface. It is IEnumerator that really allows a foreach iteration, and it's the type returned
by GetEnumerator() method in the IEnumerable interface. If you're dealing with other collections, such as an Arraylist
in the preceding example, you won't need to implement your own enumerators. However, if you want to either 1) improve
performance and/or 2) specifically control how your collection is enumerated through, then you'll want to implement your
own enumerator via the IEnumerator interface.
The IEnumerator interface implements the MoveNext() and Reset() mehtods and the Current property.
The MoveNext() method should return true IF moving to the next ordinal in the collection/list of objects is possible;
otherwise, it should return false. The Reset() method should reset the ordinal/index to a baseline value (in this case -1).
The Current property returns the object in the collection/list at the current oridinal/index.
Below is an example of a class that implements both the IEnumerator and IEnumerable interfaces. The underlying
collection is an array of Strings (System namespace). This class does two things: 1) It returns an IEnumerator object
that makes a foreach iteration possible, and 2) it implements this IEnumerator itself.
public MyMethod()
{
MyABCs myABCs = new MyABCs();
foreach (Object o in myABCs)
{
// This would not be possible IF MyABCs did NOT
// implement the IEnumerable interface!
// Note: You do NOT need to implement IEnumerable
// in order to make a foreach iteration possible.
// All you need is to have a GetEnumerator() method
// present.
}
}
public class MyABCs : IEnumerator, IEnumerable
{
// This field stores the current index/ordinal:
private int intCurrentIndex = -1;
// This array stores the underlying collection:
private String[] arrStrings;
// This is the constructor for this class:
public MyABCs()
{
this.arrStrings = new String[3];
this.arrStrings[0] = "A";
this.arrStrings[1] = "B";
this.arrStrings[2] = "C";
}
// This property implements the IEnumerator interface:
public object Current
{
get
{
if (this.intCurrentIndex > -1 &&
this.intCurrentIndex <= (this.arrStrings.Length - 1))
return this.arrStrings[this.intCurrentIndex];
else
return null;
}
}
// This method implements the IEnumerator interface:
public bool MoveNext()
{
if ((this.intCurrentIndex + 1) <= (this.arrStrings.Length - 1))
{
this.intCurrentIndex++;
return true;
}
else
return false;
}
// This method implements the IEnumerator interface:
public void Reset()
{
this.intCurrentIndex = -1;
}
// This method implements the IEnumerable interface:
public IEnumerator GetEnumerator()
{
return this;
}
}
The preceding class does not need to implement its own IEnumerator members because the underlying collection,
String[], already contains a GetEnumerator() method that makes a foreach iteration possible. Therefore, we could
rewrite the preceding class as follows:
public class MyABCs : IEnumerable
{
// This array stores the underlying collection:
private String[] arrStrings;
// This is the constructor for this class:
public MyABCs()
{
this.arrStrings = new String[3];
this.arrStrings[0] = "A";
this.arrStrings[1] = "B";
this.arrStrings[2] = "C";
}
// This method implements the IEnumerable interface:
public IEnumerator GetEnumerator()
{
return this.arrStrings.GetEnumerator();
}
}
As a matter of fact, the two preceding examples above do not even need to implement any of the two interfaces. So long as
they contain all of the necessary methods and properties that makes foreach iteration possible, they can work just
fine without them having to implement any interfaces.
public class MyABCs
{
// This array stores the underlying collection:
private String[] arrStrings;
// This is the constructor for this class:
public MyABCs()
{
this.arrStrings = new String[3];
this.arrStrings[0] = "A";
this.arrStrings[1] = "B";
this.arrStrings[2] = "C";
}
// This method implements the IEnumerable interface:
public IEnumerator GetEnumerator()
{
return this.arrStrings.GetEnumerator();
}
}
The problem with not implementing interfaces in this way is that COM compliant components will not be able to iterate
through the collection in such classes and structs. Not only that, but you won't even be able to cast the class or struct
to a generic IEnumerator or IEnumerable object.
public MyMethod()
{
MyABCs myABCs = new MyABCs();
IEnumerable oEnum = myABCs; // Oops! Error! MyABCs doesn't implement IEnumerable!
// "Cannot implicitly convert type 'MyABCs' to 'System.Collections.IEnumerable'"
foreach (Object o in oEnum)
{
}
}
public class MyABCs
{
// This array stores the underlying collection:
private String[] arrStrings;
// This is the constructor for this class:
public MyABCs()
{
this.arrStrings = new String[3];
this.arrStrings[0] = "A";
this.arrStrings[1] = "B";
this.arrStrings[2] = "C";
}
// This method implements the IEnumerable interface:
public IEnumerator GetEnumerator()
{
return this.arrStrings.GetEnumerator();
}
}
So, it's best to always implement an interface so that interoperability and compatibility can and will be maintained.
If you don't, Visual Basic and other COM-compliant technologies will not be able to iterate through the collections.