How to make a Visual Basic .NET or Visual Basic 2005 class usable in a For Each statement (322025)



The information in this article applies to:

  • Microsoft Visual Basic 2005
  • Microsoft Visual Basic .NET (2003)
  • Microsoft Visual Basic .NET (2002)

This article was previously published under Q322025
For a Microsoft Visual C# .NET version of this article, see 322022.

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.
  1. Follow these steps to create a new Console Application project in Visual Basic .NET or in Visual Basic 2005:
    1. Start Microsoft Visual Studio .NET or Microsoft Visual Studio 2005.
    2. On the File menu, point to New, and then click Project.
    3. 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.
    4. In the Name box, type ConsoleEnum.
  2. 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
    					
  3. On the Project menu, click Add Class, and then type car in the Name box.
  4. 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
    					
  5. On the Project menu, click Add Class to add another class to the project, and then type cars in the Name box.
  6. 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
    					
  7. 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
				

Modification Type:MinorLast Reviewed:10/3/2006
Keywords:kbvs2005applies kbvs2005swept kbHOWTOmaster KB322025 kbAudDeveloper