# 反覆運算器（Visual Basic）Iterators (Visual Basic)

「迭代器」可用來逐步執行集合，例如清單和陣列。An iterator can be used to step through collections such as lists and arrays.

Sub Main()
For Each number As Integer In SomeNumbers()
Console.Write(number & " ")
Next
' Output: 3 5 8
End Sub

Private Iterator Function SomeNumbers() As System.Collections.IEnumerable
Yield 3
Yield 5
Yield 8
End Function


Visual Basic 反覆運算器函數或 get 存取子宣告包含iterator修飾詞。A Visual Basic iterator function or get accessor declaration includes an Iterator modifier.

## 簡易迭代器Simple Iterator

Sub Main()
For Each number As Integer In EvenSequence(5, 18)
Console.Write(number & " ")
Next
' Output: 6 8 10 12 14 16 18
End Sub

Private Iterator Function EvenSequence(
ByVal firstNumber As Integer, ByVal lastNumber As Integer) _
As System.Collections.Generic.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

GetEnumerator 方法會使用 Yield 語句，一次傳回一個字串，而 Iterator 修飾詞則是在函式宣告中。The GetEnumerator method returns each string one at a time by using the Yield statement, and 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 Thu Fri Sat
End Sub

Private Class DaysOfTheWeek
Implements IEnumerable

Public days =
New String() {"Sun", "Mon", "Tue", "Wed", "Thu", "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


Sub Main()
Dim theZoo As New Zoo()

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

End Sub

Public Class Zoo
Implements IEnumerable

' Private members.
Private animals As New List(Of Animal)

' Public methods.
Public Sub AddMammal(ByVal name As String)
animals.Add(New Animal With {.Name = name, .Type = Animal.TypeEnum.Mammal})
End Sub

Public Sub AddBird(ByVal name As String)
animals.Add(New Animal With {.Name = name, .Type = Animal.TypeEnum.Bird})
End Sub

Public Iterator Function GetEnumerator() As IEnumerator _
Implements IEnumerable.GetEnumerator

For Each theAnimal As Animal In animals
Yield theAnimal.Name
Next
End Function

' Public members.
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


## Try 區塊Try Blocks

Visual Basic 允許在 Try 的 Try 區塊中使用 Yield 語句 ... Catch 。Finally 語句Visual Basic allows a Yield statement in the Try block of a Try...Catch...Finally Statement. 具有 Yield 語句的 Try 區塊可以有 Catch 區塊，而且可以有 Finally 區塊。A Try block that has a Yield statement can have Catch blocks, and can have a Finally block.

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.
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


Yield 語句不能在 Catch 區塊或 Finally 區塊內。A Yield statement cannot be inside a Catch block or a Finally block.

## 匿名方法Anonymous Methods

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


Sub Main()
For Each number As Integer In GetSequence(5, 10)
Console.Write(number & " ")
Next
' Output: 5 6 7 8 9 10
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


## 搭配泛型清單使用迭代器Using Iterators with a Generic List

BottomToTop 屬性宣告包含 Iterator 關鍵字。The BottomToTop property declaration includes the Iterator keyword.

Sub Main()
Dim theStack As New Stack(Of Integer)

' Add items to the stack.
For number As Integer = 0 To 9
theStack.Push(number)
Next

' Retrieve items from the stack.
' For Each is allowed because theStack implements
' IEnumerable(Of Integer).
For Each number As Integer In theStack
Console.Write("{0} ", number)
Next
Console.WriteLine()
' Output: 9 8 7 6 5 4 3 2 1 0

' For Each is allowed, because theStack.TopToBottom
' returns IEnumerable(Of Integer).
For Each number As Integer In theStack.TopToBottom
Console.Write("{0} ", number)
Next
Console.WriteLine()
' Output: 9 8 7 6 5 4 3 2 1 0

For Each number As Integer In theStack.BottomToTop
Console.Write("{0} ", number)
Next
Console.WriteLine()
' Output: 0 1 2 3 4 5 6 7 8 9

For Each number As Integer In theStack.TopN(7)
Console.Write("{0} ", number)
Next
Console.WriteLine()
' Output: 9 8 7 6 5 4 3

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


## 語法資訊Syntax Information

Yield 陳述式中的運算式類型必須隱含轉換成迭代器的傳回型別。An implicit conversion must exist from the expression type in the Yield statement to the return type of the iterator.

## 迭代器的使用Use of Iterators

• 在第一次反覆運算 For Each 迴圈之後修改清單序列。Modify the list sequence after the first For Each loop iteration.

• 避免在第一次反覆運算 For Each 迴圈之前完整載入大型清單。Avoid fully loading a large list before the first iteration of a For Each loop. 分頁擷取以分批載入資料表資料列即為一例。An example is a paged fetch to load a batch of table rows. 另一個範例是 EnumerateFiles 方法，它會在 .NET Framework 中實作迭代器。Another example is the EnumerateFiles method, which implements iterators within the .NET Framework.

• 在迭代器中封裝建立清單。Encapsulate building the list in the iterator. 在迭代器方法中，您可以建立清單，然後在迴圈中產生每個結果。In the iterator method, you can build the list and then yield each result in a loop.