SUMMARY
This step-by-step article demonstrates how to use the
IEnumerable and the
IEnumerator interfaces to create a class that you can use in a
For Each...Next statement.
IEnumerable and
IEnumerator are frequently used together. Although these interfaces are similar (and have similar names), they have different purposes.
IEnumerator Interface
The
IEnumerator interface provides iterative capability for a collection that is internal to a class.
IEnumerator requires that you implement three methods:
- The MoveNext method, which increments the collection index by 1 and returns a bool that indicates whether the end of the collection has been reached.
- The Reset method, which resets the collection index to its initial value of -1.
- The Current method, which returns the current object at [position].
Public Function MoveNext() As Boolean Implements IEnumerator.MoveNext
position += 1
Return (position < carlist.Length)
End Function
Public Sub Reset() Implements IEnumerator.Reset
position = -1
End Sub
Public ReadOnly Property Current() As Object Implements IEnumerator.Current
Get
Return carlist(position)
End Get
End Property
IEnumerable Interface
The
IEnumerable interface provides support for the
For Each iteration.
IEnumerable requires that you implement the
GetEnumerator method.
Public Function GetEnumerator() As IEnumerator Implements IEnumerable.GetEnumerator
Return CType(Me, IEnumerator)
End Function
When to Use Which Interface
Initially, you may find it confusing to use these interfaces. The
IEnumerator interface provides iteration over a collection-type object in a class. The
IEnumerable interface permits enumeration by using a
For Each loop. However, the
GetEmunerator method of the
IEnumerable interface returns an
IEnumerator interface. Therefore, to implement
IEnumerable, you must also implement
IEnumerator. If you do not implement
IEnumerator, you cannot cast the return value from the
GetEnumerator method of
IEnumerable to the
IEnumerator interface.
In summary, the use of
IEnumerable requires that the class implement
IEnumerator. If you want to provide support for
For Each, implement both interfaces.
Step-by-Step Example
The following example demonstrates how to use these interfaces. In this example, the
IEnumerator and the
IEnumerable interfaces are used in a class that is named
cars. The
cars class has an internal array of
car objects. Client applications can enumerate through this internal array by using a
For Each construct because of the implementation of these two interfaces.
- Follow these steps to create a new Console Application project in Visual Basic .NET or in Visual Basic 2005:
- Start Microsoft Visual Studio .NET or Microsoft Visual Studio 2005.
- On the File menu, point to New, and then click Project.
- Click Visual Basic Projects under Project Types, and then click Console Application under Templates.
Note In Visual Studio 2005, click Visual Basic under Project Types. - In the Name box, type ConsoleEnum.
- Replace the code in Module1.vb with the following code:
Module Module1
Sub Main()
Dim c As car
Dim t As String = vbTab & vbTab
Dim n As String = vbCrLf
Dim carz As cars = New cars()
Console.WriteLine(n & "Internal Collection" & n)
For Each c In carz
Console.WriteLine(c.Make & t & c.Year)
Next
Console.ReadLine()
End Sub
End Module
- On the Project menu, click Add Class, and then type car in the Name box.
- Replace the code in car.vb with the following code:
Option Explicit On
Option Strict On
Imports System.Collections
Public Class car
Private myear As Integer
Private mmake As String
Public Sub New(ByVal Make As String, ByVal Year As Integer)
mmake = Make
myear = Year
End Sub
Public Property Year() As Integer
Get
Return myear
End Get
Set(ByVal Value As Integer)
myear = Value
End Set
End Property
Public Property Make() As String
Get
Return mmake
End Get
Set(ByVal Value As String)
mmake = Value
End Set
End Property
End Class
- On the Project menu, click Add Class to add another class to the project, and then type cars in the Name box.
- Replace the code in cars.vb with the following code:
Imports System.Collections
Public Class cars : Implements IEnumerator, IEnumerable
Private position As Integer = -1
Private carlist() As car = _
{ _
New car("Ford", 1992), _
New car("Fiat", 1988), _
New car("Buick", 1932), _
New car("Ford", 1932), _
New car("Dodge", 1999), _
New car("Honda", 1977) _
}
Public Sub New()
End Sub
Public Function GetEnumerator() As IEnumerator Implements IEnumerable.GetEnumerator
Return CType(Me, IEnumerator)
End Function
Public Function MoveNext() As Boolean Implements IEnumerator.MoveNext
position += 1
Return (position < carlist.Length)
End Function
Public Sub Reset() Implements IEnumerator.Reset
position = -1
End Sub
Public ReadOnly Property Current() As Object Implements IEnumerator.Current
Get
Return carlist(position)
End Get
End Property
End Class
- Run the project. Notice that the following output appears in the Console window:
Ford 1992
Fiat 1988
Buick 1932
Ford 1932
Dodge 1999
Honda 1977
Best Practices
The example in this article is kept as simple as possible to better explain the use of these interfaces. To make the code more robust and to make sure that the code uses the current best practice guidelines, modify the code as follows:
- Implement IEnumerator in a nested class so that you can create multiple enumerators.
- Provide exception handling for the Current method of IEnumerator. If the contents of the collection change, the reset method is called. As a result, the current enumerator is invalidated, and you receive an IndexOutOfRangeException exception. Other circumstances may also cause this exception. Therefore, implement a Try...Catch block to catch this exception and to raise an InvalidOperationException exception.
Imports System.Collections
Public Class cars : Implements IEnumerable
Private position As Integer = -1
Private carlist() As car = _
{ _
New car("Ford", 1992), _
New car("Fiat", 1988), _
New car("Buick", 1932), _
New car("Ford", 1932), _
New car("Dodge", 1999), _
New car("Honda", 1977) _
}
'private enumerator class
Private Class MyEnumerator : Implements IEnumerator
Private carlist As car()
Private position As Integer = -1
Public Sub New(ByVal list)
carlist = list
End Sub
Public Function MoveNext() As Boolean _
Implements IEnumerator.MoveNext
position += 1
Return (position < carlist.Length)
End Function
Public Sub Reset() Implements IEnumerator.Reset
position = -1
End Sub
Public ReadOnly Property Current() As Object _
Implements IEnumerator.Current
Get
Try
Return carlist(position)
Catch e As IndexOutOfRangeException
Throw New InvalidOperationException()
End Try
End Get
End Property
End Class ' end of nested class
Public Function GetEnumerator() As IEnumerator _
Implements IEnumerable.GetEnumerator
Return New MyEnumerator(carlist)
End Function
End Class