Skip to main content

Iterators in Visual Basic

An iterator in Visual Basic can be used to step through collections such as lists and arrays.

An iterator function or get accessor performs a custom iteration over a collection. An iterator function uses the Yield statement to return each element in turn. When a Yield statement is reached, the current location is remembered. Execution is restarted from that location the next time the iterator function is called.

The return type of an iterator function or get accessor can be IEnumerable,IEnumerable(T)IEnumerator, or IEnumerator(T). An Iterator modifier is included in the function or get accessor declaration.

An Exit Function or Return statement can be used to end the iteration. They are the equivalent of a yield break statement in C#.

Note: Iterators were introduced in C# in Visual Studio 2005. For more information, see Iterators (C# Programming Guide).

Simple Iterators

The following Visual Basic example includes multiple Yield statements. Each iteration of the For Each statement body creates a call to the iterator function, which goes to the next Yield statement.

Sub Main()

    For Each number As Integer In SomeNumbers()

        Console.Write(number & " ")

    Next

    ' Output: 3 5 8

    Console.ReadKey()

End Sub



Private Iterator Function SomeNumbers() As IEnumerable

    ' Use multiple yield statements.

    Yield 3

    Yield 5

    Yield 8

End Function


The following example has a single Yield statement that is inside a For…Next loop. Each iteration of the For Each statement body in Main creates a call to the iterator function, which proceeds to the next Yield statement.

Sub Main()

    For Each number As Integer In EvenSequence(5, 18)

        Console.Write(number & " ")

    Next

    ' Output: 6 8 10 12 14 16 18

    Console.ReadKey()

End Sub



Private Iterator Function EvenSequence(

ByVal firstNumber As Integer, ByVal lastNumber As Integer) _

As IEnumerable(Of Integer)



    ' Yield even numbers in the range.

    For number As Integer = firstNumber To lastNumber

        If number Mod 2 = 0 Then

            Yield number

        End If

    Next

End Function

Creating a Collection Class

In the following example, the DaysOfTheWeek class implements the IEnumerable interface, which requires a GetEnumeratorfunction. The compiler implicitly calls the GetEnumerator function, which returns an IEnumerator.

The GetEnumerator function returns each string in turn by using the Yield statement. An Iterator modifier is in the function declaration.

Sub Main()

    Dim days As New DaysOfTheWeek()

    For Each day As String In days

        Console.Write(day & " ")

    Next

    ' Output: Sun Mon Tue Wed Thr Fri Sat

    Console.ReadKey()

End Sub



Private Class DaysOfTheWeek

    Implements IEnumerable



    Public days =

        New String() {"Sun", "Mon", "Tue", "Wed", "Thr", "Fri", "Sat"}



    Public Iterator Function GetEnumerator() As IEnumerator _

        Implements IEnumerable.GetEnumerator



        ' Yield each day of the week.

        For i As Integer = 0 To days.Length - 1

            Yield days(i)

        Next

    End Function

End Class


The following example creates a Zoo class that contains a collection of animals.

The For Each statement that refers to the class instance (theZoo) implicitly calls the GetEnumerator function. The For Each statements that refer to the Birds and Mammals properties use the AnimalsForType named iterator function.

Sub Main()

    Dim theZoo As New Zoo()



    theZoo.AddMammal("Whale")

    theZoo.AddMammal("Rhinoceros")

    theZoo.AddBird("Penguin")

    theZoo.AddBird("Warbler")



    For Each name As String In theZoo

        Console.Write(name & " ")

    Next

    Console.WriteLine()

    ' Output: Whale Rhinoceros Penguin Warbler



    For Each name As String In theZoo.Birds

        Console.Write(name & " ")

    Next

    Console.WriteLine()

    ' Output: Penguin Warbler



    For Each name As String In theZoo.Mammals

        Console.Write(name & " ")

    Next

    Console.WriteLine()

    ' Output: Whale Rhinoceros



    Console.ReadKey()

End Sub



Public Class Zoo

    Implements IEnumerable



    Private animals As New List(Of Animal)



    ' Constructors.

    Public Sub AddMammal(ByVal name As String)

        Dim thisAnimal As New Animal() _

            With {.Name = name, .Type = Animal.TypeEnum.Mammal}

        animals.Add(thisAnimal)

    End Sub



    Public Sub AddBird(ByVal name As String)

        Dim thisAnimal As New Animal() _

            With {.Name = name, .Type = Animal.TypeEnum.Bird}

        animals.Add(thisAnimal)

    End Sub



    ' Public methods and members.

    Public Iterator Function GetEnumerator() As IEnumerator _

        Implements IEnumerable.GetEnumerator



        For Each theAnimal As Animal In animals

            Yield theAnimal.Name

        Next

    End Function



    Public ReadOnly Property Mammals As IEnumerable

        Get

            Return AnimalsForType(Animal.TypeEnum.Mammal)

        End Get

    End Property



    Public ReadOnly Property Birds As IEnumerable

        Get

            Return AnimalsForType(Animal.TypeEnum.Bird)

        End Get

    End Property



    ' Private methods.

    Private Iterator Function AnimalsForType( _

    ByVal type As Animal.TypeEnum) As IEnumerable

        For Each theAnimal As Animal In animals

            If (theAnimal.Type = type) Then

                Yield theAnimal.Name

            End If

        Next

    End Function



    ' Private class.

    Private Class Animal

    Public Enum TypeEnum

            Bird

            Mammal

        End Enum



        Public Property Name As String

        Public Property Type As TypeEnum

    End Class

End Class



            Public Property Name As String

            Public Property Type As TypeEnum

        End Class

    End Class

Try Blocks

Unlike C#, Visual Basic allows a Yield statement in the Try Block of a Try…Catch...Finally statement.

The following example includes Try, Catch, and Finally blocks in an iterator function. The Finally block in the iterator function executes before the For Each iteration finishes.

Sub Main()

    For Each number As Integer In Test()

        Console.WriteLine(number)

    Next

    Console.WriteLine("For Each is done.")



    ' Output:

    '  3

    '  4

    '  Something happened. Yields are done.

    '  Finally is called.

    '  For Each is done.

    Console.ReadKey()

End Sub



Private Iterator Function Test() As IEnumerable(Of Integer)

    Try

        Yield 3

        Yield 4

        Throw New Exception("Something happened. Yields are done.")

        Yield 5

        Yield 6

    Catch ex As Exception

        Console.WriteLine(ex.Message)

    Finally

        Console.WriteLine("Finally is called.")

    End Try

End Function


If the For Each body instead throws an exception, a Catch block in the iterator function is not executed, but a Finally block in the iterator function is executed. A Catch block inside an iterator function catches only exceptions that occur inside the iterator function.

Anonymous Methods

In Visual Basic, an anonymous function can be an iterator function. The function is invoked by the Invoke method. The following example illustrates this.

Dim iterateSequence = Iterator Function() As IEnumerable(Of Integer)

               Yield 1

               Yield 2

           End Function



For Each number As Integer In iterateSequence()

    Console.Write(number & " ")

Next

' Output: 1 2

Console.ReadKey()


The following example has a non-iterator method that validates the arguments. The method returns the result of an anonymous iterator that describes the collection elements.

Sub Main()

    For Each number As Integer In GetSequence(5, 10)

        Console.Write(number & " ")

    Next

    ' Output: 5 6 7 8 9 10

    Console.ReadKey()

End Sub



Public Function GetSequence(

ByVal low As Integer, ByVal high As Integer) As IEnumerable

    ' Validate the arguments.

    If low < 1 Then

        Throw New ArgumentException("low is too low")

    End If

    If high > 140 Then

        Throw New ArgumentException("high is too high")

    End If



    ' Return an anonymous iterator function.

    Dim iterateSequence = Iterator Function() As IEnumerable

                   For index = low To high

                       Yield index

                   Next

               End Function

    Return iterateSequence()

End Function


If validation is instead inside the iterator function, the validation cannot be performed until the start of the first iteration of the For Each body.

Using Iterators with a Generic List

In the following example, the Stack(Of T) generic class implements the IEnumerable(T) generic interface. The Push method assigns values to an array of type T. The GetEnumerator method returns the array values by using the Yield statement.

In addition to the generic IEnumerable(T) GetEnumerator method, the non-generic GetEnumerator method must also be implemented. This is because IEnumerable(T) inherits from IEnumerable. The non-generic implementation defers to the generic implementation.

The BottomToTop property has an example of a get accessor in an Iterator Property declaration.

Sub Main()

    Dim stk As New Stack(Of Integer)



    ' Add items to the stack.

    For number As Integer = 0 To 9

        stk.Push(number)

    Next



    ' Retrieve items from the stack.

    ' For Each is allowed because stk implements

    ' IEnumerable(Of Integer).

    For Each number As Integer In stk

        System.Console.Write("{0} ", number)

    Next

    System.Console.WriteLine()

    ' Output: 9 8 7 6 5 4 3 2 1 0



    ' For Each is allowed, because stk.TopToBottom returns

    ' IEnumerable(Of Integer).

    For Each number As Integer In stk.TopToBottom

        System.Console.Write("{0} ", number)

    Next

    System.Console.WriteLine()

    ' Output: 9 8 7 6 5 4 3 2 1 0



    For Each number As Integer In stk.BottomToTop

        System.Console.Write("{0} ", number)

    Next

    System.Console.WriteLine()

    ' Output: 0 1 2 3 4 5 6 7 8 9 



    For Each number As Integer In stk.TopN(7)

        System.Console.Write("{0} ", number)

    Next

    System.Console.WriteLine()

    ' Output: 9 8 7 6 5 4 3



    System.Console.ReadKey()

End Sub





Public Class Stack(Of T)

    Implements IEnumerable(Of T)



    Private values As T() = New T(99) {}

    Private top As Integer = 0



    Public Sub Push(ByVal t As T)

        values(top) = t

        top = top + 1

    End Sub



    Public Function Pop() As T

        top = top - 1

        Return values(top)

    End Function



    ' This function implements the GetEnumerator method. It allows

    ' an instance of the class to be used in a For Each statement.

    Public Iterator Function GetEnumerator() As IEnumerator(Of T) _

        Implements IEnumerable(Of T).GetEnumerator



        For index As Integer = top - 1 To 0 Step -1

            Yield values(index)

        Next

    End Function



    Public Iterator Function GetEnumerator1() As IEnumerator _

        Implements IEnumerable.GetEnumerator



        Yield GetEnumerator()

    End Function



    Public ReadOnly Property TopToBottom() As IEnumerable(Of T)

        Get

            Return Me

        End Get

    End Property



    Public ReadOnly Iterator Property BottomToTop As IEnumerable(Of T)

        Get

            For index As Integer = 0 To top - 1

                Yield values(index)

            Next

        End Get

    End Property



    Public Iterator Function TopN(ByVal itemsFromTop As Integer) _

        As IEnumerable(Of T)



        ' Return less than itemsFromTop if necessary.

        Dim startIndex As Integer =

            If(itemsFromTop >= top, 0, top - itemsFromTop)



        For index As Integer = top - 1 To startIndex Step -1

            Yield values(index)

        Next

    End Function



End Class