迭代器 (Visual Basic)Iterators (Visual Basic)
「迭代器」可用來逐步執行集合,例如清單和陣列。An iterator can be used to step through collections such as lists and arrays.
迭代器方法或 get
存取子會對集合執行自訂反覆運算。An iterator method or get
accessor performs a custom iteration over a collection. Iterator 方法會使用 Yield 語句,一次傳回一個元素。An iterator method uses the Yield statement to return each element one at a time. 當到達 Yield
陳述式時,系統會記住程式碼中的目前位置。When a Yield
statement is reached, the current location in code is remembered. 下次呼叫迭代器函式時,便會從這個位置重新開始執行。Execution is restarted from that location the next time the iterator function is called.
您可以使用 For Each 的來從用戶端程式代碼取用反覆運算器。 Next 語句,或是使用 LINQ 查詢。You consume an iterator from client code by using a For Each…Next statement, or by using a LINQ query.
在下列範例中,第一次反覆運算 For Each
迴圈會使 SomeNumbers
迭代器方法中的執行繼續,直到到達第一個 Yield
陳述式為止。In the following example, the first iteration of the For Each
loop causes execution to proceed in the SomeNumbers
iterator method until the first Yield
statement is reached. 此反覆運算會傳回值 3,並保留迭代器方法中的目前位置。This iteration returns a value of 3, and the current location in the iterator method is retained. 下次反覆運算迴圈時,迭代器方法中的執行會從上次停止的位置繼續,並且在到達 Yield
陳述式時再次停止。On the next iteration of the loop, execution in the iterator method continues from where it left off, again stopping when it reaches a Yield
statement. 此反覆運算會傳回值 5,並再次保留迭代器方法中的目前位置。This iteration returns a value of 5, and the current location in the iterator method is again retained. 當到達迭代器方法結尾時,迴圈便完成。The loop completes when the end of the iterator method is reached.
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 System.Collections.IEnumerable
Yield 3
Yield 5
Yield 8
End Function
迭代器方法或 get
存取子的傳回型別可以是 IEnumerable、IEnumerable<T>、IEnumerator 或 IEnumerator<T>。The return type of an iterator method or get
accessor can be IEnumerable, IEnumerable<T>, IEnumerator, or IEnumerator<T>.
您可以使用 Exit Function
或 Return
語句來結束反復專案。You can use an Exit Function
or Return
statement to end the iteration.
Visual Basic 反覆運算器函數或 get
存取子宣告包含 iterator 修飾詞。A Visual Basic iterator function or get
accessor declaration includes an Iterator modifier.
反覆運算器是在 Visual Basic 的 Visual Studio 2012 中引進。Iterators were introduced in Visual Basic in Visual Studio 2012.
本主題內容In this topic
注意
針對簡單反覆運算器範例以外主題中的所有範例,請包含 System.Collections
和命名空間的 Imports 語句 System.Collections.Generic
。For all examples in the topic except the Simple Iterator example, include Imports statements for the System.Collections
and System.Collections.Generic
namespaces.
Simple IteratorSimple Iterator
下列範例的單一語句位於 Yield
For .。。Next 迴圈。The following example has a single Yield
statement that is inside a For…Next loop. 在 Main
中,每次反覆運算 For Each
陳述式主體都會建立迭代器函式的呼叫,以繼續進行下一個 Yield
陳述式。In Main
, each iteration of the For Each
statement body 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 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
在以下範例中,DaysOfTheWeek
類別會實作 IEnumerable 介面,而這個介面需使用 GetEnumerator 方法。In the following example, the DaysOfTheWeek
class implements the IEnumerable interface, which requires a GetEnumerator method. 編譯器會隱含呼叫 GetEnumerator
方法,以傳回 IEnumerator。The compiler implicitly calls the GetEnumerator
method, which returns an IEnumerator.
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
Console.ReadKey()
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
下列範例會建立 Zoo
類別,其中包含動物的集合。The following example creates a Zoo
class that contains a collection of animals.
參考類別執行個體 (theZoo
) 的 For Each
陳述式會隱含呼叫 GetEnumerator
方法。The For Each
statement that refers to the class instance (theZoo
) implicitly calls the GetEnumerator
method. 參考 Birds
和 Mammals
屬性的 For Each
陳述式會使用 AnimalsForType
具名迭代器方法。The For Each
statements that refer to the Birds
and Mammals
properties use the AnimalsForType
named iterator method.
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 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 允許 Yield
Try
Try 區塊中的語句 ... 抓住。。。Finally 語句。Visual Basic allows a Yield
statement in the Try
block of a Try...Catch...Finally Statement. Try
具有語句的區塊 Yield
可以有 Catch
區塊,而且可以有 Finally
區塊。A Try
block that has a Yield
statement can have Catch
blocks, and can have a Finally
block.
下列範例包含 Try
Catch
Finally
iterator 函數中的、和區塊。The following example includes Try
, Catch
, and Finally
blocks in an iterator function. 反覆運算 Finally
器函式中的區塊會在 For Each
反覆運算完成之前執行。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
Yield
語句不能在 Catch
區塊或 Finally
區塊內。A Yield
statement cannot be inside a Catch
block or a Finally
block.
如果 For Each
主體 (而非 iterator 方法) 擲回例外狀況, Catch
則不會執行 iterator 函數中的區塊,但 Finally
會執行 iterator 函數中的區塊。If the For Each
body (instead of the iterator method) throws an exception, a Catch
block in the iterator function is not executed, but a Finally
block in the iterator function is executed. Catch
反覆運算器函式內的區塊只會捕捉反覆運算器函式內發生的例外狀況。A Catch
block inside an iterator function catches only exceptions that occur inside the iterator function.
匿名方法Anonymous Methods
在 Visual Basic 中,匿名函數可以是 iterator 函數。In Visual Basic, an anonymous function can be an iterator function. 下列範例將說明這點。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
如果驗證是在反覆運算器函式內,則在主體的第一次反覆運算開始之前,無法執行驗證 For Each
。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
在以下範例中,Stack(Of T)
泛型類別會實作 IEnumerable<T> 泛型介面。In the following example, the Stack(Of T)
generic class implements the IEnumerable<T> generic interface. Push
方法會將值指派給 T
類型的陣列。The Push
method assigns values to an array of type T
. GetEnumerator 方法會使用 Yield
陳述式以傳回陣列值。The GetEnumerator method returns the array values by using the Yield
statement.
除了泛型 GetEnumerator 方法,您也必須實作非泛型 GetEnumerator 方法。In addition to the generic GetEnumerator method, the non-generic GetEnumerator method must also be implemented. 這是因為 IEnumerable<T> 繼承自 IEnumerable。This is because IEnumerable<T> inherits from IEnumerable. 非泛型實作會延後到泛型實作。The non-generic implementation defers to the generic implementation.
此範例使用具名迭代器來支援逐一查看相同資料集合的各種方法。The example uses named iterators to support various ways of iterating through the same collection of data. 這些具名迭代器是 TopToBottom
和 BottomToTop
屬性,以及 TopN
方法。These named iterators are the TopToBottom
and BottomToTop
properties, and the TopN
method.
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
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
語法資訊Syntax Information
出現的迭代器可以是方法或 get
存取子。An iterator can occur as a method or get
accessor. 迭代器不能出現在事件、執行個體建構函式、靜態建構函式或靜態解構函式中。An iterator cannot occur in an event, instance constructor, static constructor, or static destructor.
Yield
陳述式中的運算式類型必須隱含轉換成迭代器的傳回型別。An implicit conversion must exist from the expression type in the Yield
statement to the return type of the iterator.
在 Visual Basic 中,反覆運算器方法不能有任何 ByRef
參數。In Visual Basic, an iterator method cannot have any ByRef
parameters.
在 Visual Basic 中,"Yield" 不是保留字,而且只有在方法或存取子中使用時才具有特殊意義 Iterator
get
。In Visual Basic, "Yield" is not a reserved word and has special meaning only when it is used in an Iterator
method or get
accessor.
技術實行Technical Implementation
雖然您將迭代器撰寫成方法,但編譯器會將其轉譯成巢狀類別,其實也就是狀態機器。Although you write an iterator as a method, the compiler translates it into a nested class that is, in effect, a state machine. 此類別會在用戶端程式碼中的 For Each...Next
迴圈繼續期間追蹤迭代器的位置。This class keeps track of the position of the iterator as long the For Each...Next
loop in the client code continues.
若要查看編譯器的功能,您可以使用 Ildasm.exe 工具來檢視為迭代器方法產生的 Microsoft 中繼語言程式碼。To see what the compiler does, you can use the Ildasm.exe tool to view the Microsoft intermediate language code that is generated for an iterator method.
當您為 類別 或 結構建立反覆運算器時,不需要執行整個 IEnumerator 介面。When you create an iterator for a class or struct, you do not have to implement the whole IEnumerator interface. 當編譯器偵測到迭代器時,它會自動產生 IEnumerator 或 IEnumerator<T> 介面的 Current
、MoveNext
和 Dispose
方法。When the compiler detects the iterator, it automatically generates the Current
, MoveNext
, and Dispose
methods of the IEnumerator or IEnumerator<T> interface.
之後每次反覆運算 For Each…Next
迴圈 (或直接呼叫 IEnumerator.MoveNext
),下一個迭代器程式碼主體都會在上一個 Yield
陳述式之後繼續。On each successive iteration of the For Each…Next
loop (or the direct call to IEnumerator.MoveNext
), the next iterator code body resumes after the previous Yield
statement. 然後,它會繼續執行下一個 Yield
語句,直到達到 iterator 主體的結尾,或在 Exit Function
遇到或 Return
語句為止。It then continues to the next Yield
statement until the end of the iterator body is reached, or until an Exit Function
or Return
statement is encountered.
反覆運算器不支援 IEnumerator.Reset 方法。Iterators do not support the IEnumerator.Reset method. 若要從頭開始逐一查看,您必須取得新的迭代器。To re-iterate from the start, you must obtain a new iterator.
如需詳細資訊,請參閱 Visual Basic 語言規格。For additional information, see the Visual Basic Language Specification.
迭代器的使用Use of Iterators
當您需要使用複雜的程式碼來填入清單序列時,迭代器可讓您維持 For Each
迴圈的簡潔性。Iterators enable you to maintain the simplicity of a For Each
loop when you need to use complex code to populate a list sequence. 當您想要執行下列作業時,這會很有用:This can be useful when you want to do the following:
在第一次反覆運算
For Each
迴圈之後修改清單序列。Modify the list sequence after the firstFor Each
loop iteration.避免在第一次反覆運算
For Each
迴圈之前完整載入大型清單。Avoid fully loading a large list before the first iteration of aFor 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.