For Each...Next 陳述式 (Visual Basic)
針對集合中的每個元素重複一組陳述式。
語法
For Each element [ As datatype ] In group
[ statements ]
[ Continue For ]
[ statements ]
[ Exit For ]
[ statements ]
Next [ element ]
組件
詞彙 | 定義 |
---|---|
element |
For Each 陳述式中的必要項目。 Next 陳述式中的選用項目。 變數。 用來逐一查看集合的元素。 |
datatype |
如果 Option Infer 為開啟 (預設) 或已宣告 element ,則為選用;如果 Option Infer 為關閉,而且尚未宣告 element ,則為必要。 element 的資料型別。 |
group |
必要。 具有集合型別或物件型別的變數。 參考將重複的 statements 集合。 |
statements |
選擇性。 位於 For Each 和 Next 之間而且在 group 中的每個項目上執行的一個或多個陳述式。 |
Continue For |
選擇性。 將控制權傳輸至 For Each 迴圈的開頭。 |
Exit For |
選擇性。 將控制權移出 For Each 迴圈。 |
Next |
必要。 終止 For Each 迴圈的定義。 |
簡單範例
您想要針對集合或陣列的每個元素重複一組陳述式時,請使用 For Each
...Next
迴圈。
提示
您可以將迴圈的每個反覆項目與控制項變數產生關聯,並判斷該變數的初始和最終值時,For...Next 陳述式可以正常運作。 不過,您處理集合時,初始和最終值的概念並沒有意義,而且不一定知道集合有多少元素。 在這種情況下,For Each
...Next
迴圈通常是較佳的選擇。
在下列範例中,For Each
…Next
陳述式會逐一查看清單集合的所有元素。
' Create a list of strings by using a
' collection initializer.
Dim lst As New List(Of String) _
From {"abc", "def", "ghi"}
' Iterate through the list.
For Each item As String In lst
Debug.Write(item & " ")
Next
Debug.WriteLine("")
'Output: abc def ghi
巢狀迴圈
您可以將一個迴圈放在另一個迴圈內,使 For Each
迴圈形成巢狀。
下列範例示範巢狀 For Each
…Next
結構。
' Create lists of numbers and letters
' by using array initializers.
Dim numbers() As Integer = {1, 4, 7}
Dim letters() As String = {"a", "b", "c"}
' Iterate through the list by using nested loops.
For Each number As Integer In numbers
For Each letter As String In letters
Debug.Write(number.ToString & letter & " ")
Next
Next
Debug.WriteLine("")
'Output: 1a 1b 1c 4a 4b 4c 7a 7b 7c
使迴圈形成巢狀時,每個迴圈都必須有唯一的 element
變數。
您也可以將不同類型的控制結構相互內嵌。 如需詳細資訊,請參閱巢狀控制結構。
結束並繼續
Exit For 陳述式會導致執行結束 For
Next
迴圈,並將控制權轉移至 Next
陳述式之後的陳述式。
Continue For
陳述式會立即將控制權轉移至迴圈的下一次反覆運算。 如需詳細資訊,請參閱 Continue 陳述式。
下列範例顯示如何使用 Continue For
和 Exit For
陳述式。
Dim numberSeq() As Integer =
{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}
For Each number As Integer In numberSeq
' If number is between 5 and 8, continue
' with the next iteration.
If number >= 5 And number <= 8 Then
Continue For
End If
' Display the number.
Debug.Write(number.ToString & " ")
' If number is 10, exit the loop.
If number = 10 Then
Exit For
End If
Next
Debug.WriteLine("")
' Output: 1 2 3 4 9 10
您可以在 For Each
迴圈中放入任意數目的 Exit For
陳述式。 在巢狀 For Each
迴圈內使用時,Exit For
會導致執行結束最內層的迴圈,並將控制權傳輸至下一個較高的巢狀層級。
Exit For
通常會在評估某些情況之後使用,例如,在 If
...Then
...Else
結構中。 在下列情況中,您可能會想使用 Exit For
:
繼續逐一查看是不必要的或不可能的。 這可能是因為錯誤值或終止要求所造成。
Try
...Catch
...Finally
中攔截到例外狀況。您可以在Finally
區塊結尾使用Exit For
。有無限迴圈,可用於執行大量或甚至無限次數的迴圈。 如果您偵測到該情況,則可以使用
Exit For
來逸出迴圈。 如需詳細資訊,請參閱 Do...Loop 陳述式。
迭代器
您可以使用「迭代器」在集合上執行自訂反覆項目。 迭代器可以是語言函式或 Get
存取子。 這會使用 Yield
陳述式,一次一個地傳回集合中的每個元素。
您會使用 For Each...Next
陳述式來呼叫迭代器。 For Each
迴圈的每個反覆項目都會呼叫迭代器。 在迭代器中到達 Yield
陳述式時,會傳回 Yield
陳述式中的運算式,並保留程式碼中的目前位置。 下一次呼叫迭代器時,便會從這個位置重新開始執行。
下列範例使用了 iterator 語言函式。 迭代器語言函式具有 For…Next 迴圈內的 Yield
陳述式。 在 ListEvenNumbers
方法中,每次反覆運算 For Each
陳述式主體都會建立迭代器語言函式的呼叫,以繼續進行下一個 Yield
陳述式。
Public Sub ListEvenNumbers()
For Each number As Integer In EvenSequence(5, 18)
Debug.Write(number & " ")
Next
Debug.WriteLine("")
' 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 = firstNumber To lastNumber
If number Mod 2 = 0 Then
Yield number
End If
Next
End Function
技術實作
在 For Each
時…Next
陳述式會執行,Visual Basic 只會評估集合一次,迴圈才會開始。 如果您的陳述式區塊變更 element
或 group
,這些變更不會影響迴圈的反覆項目。
集合中的所有元素都已連續指派給 element
時,For Each
迴圈會停止並控制傳遞至 Next
陳述式後面的陳述式。
如果 Option Infer 開啟 (其預設設定),Visual Basic 編譯器就可以推斷 element
的資料型別。 如果已關閉且 element
尚未在迴圈外部宣告,您必須在 For Each
陳述式中宣告它。 若要明確宣告 element
的資料型別,請使用 As
子句。 除非元素的資料型別定義在 For Each
... Next
建構之外,否則其範圍是迴圈的主體。 請注意,您無法在迴圈外部和內部宣告 element
。
您可以選擇在 Next
陳述式中指定 element
。 這可改善程式的可讀性,在您擁有巢狀 For Each
迴圈的情況下更是如此。 您必須指定與對應 For Each
陳述式中出現的變數相同的變數。
您可能想要避免在迴圈內變更 element
的值。 這麼做會讓您更難以閱讀和偵錯您的程式碼。 變更 group
的值不會影響第一次輸入迴圈時所決定的集合或其元素。
您使迴圈成為巢狀時,如果在內部層級的 Next
之前遇到外部巢狀層級的 Next
陳述式,編譯器就會發出錯誤訊號。 不過,只有在您在每個 Next
陳述式中指定 element
時,編譯器才能偵測到這個重迭的錯誤。
如果您的程式碼需要以特定順序周遊集合,除非您知道集合所公開的列舉值物件特性,否則 For Each
...Next
迴圈不是最佳選擇。 周遊的順序不是由 Visual Basic 決定,而是由列舉值物件的 MoveNext 方法決定。 因此,您可能無法預測 element
會先傳回集合中的哪個元素,或將在指定的元素之後要接著傳回哪個元素。 您可以使用不同的迴圈結構來達到更可靠的結果,例如 For
... Next
或 Do
... Loop
。
執行階段必須能夠將 group
中的元素轉換成 element
。 [Option Strict
] 陳述式可控制是否允許擴大和縮小轉換 (Option Strict
關閉,其預設值),或是否允許放大轉換 (Option Strict
開啟)。 如需詳細資訊,請參閱縮小轉換。
group
的資料型別必須是參考集合的參考型別,或是可列舉的陣列。 最常見的情況是,這表示 group
參考實作 System.Collections
命名空間 IEnumerable 介面或 System.Collections.Generic
命名空間 IEnumerable<T> 介面的物件。 System.Collections.IEnumerable
定義 GetEnumerator 方法,這個方法會傳回集合的列舉值物件。 列舉值物件會實作 System.Collections
命名空間 System.Collections.IEnumerator
介面,並公開 Current 屬性以及 Reset 和 MoveNext 方法。 Visual Basic 會使用這些來周遊集合。
縮小轉換
Option Strict
設定為 On
時,縮小轉換通常會造成編譯器錯誤。 不過,在 For Each
陳述式中,從 group
中的元素轉換為 element
的過程會在執行階段進行評估和執行,並隱藏縮小轉換所造成的編譯器錯誤。
在下列範例中,Option Strict
開啟時,做為 n
的初始值而指派 的 m
不會編譯,因為 Long
轉換成 Integer
的過程是縮小轉換。 不過,在 For Each
陳述式中,即使指派到 number
需要從 Long
到 Integer
的相同轉換,也不會報告編譯器錯誤。 在包含大量資料的 For Each
陳述式中,ToInteger 套用至大量資料時,就會發生執行階段錯誤。
Option Strict On
Imports System
Module Program
Sub Main(args As String())
' The assignment of m to n causes a compiler error when
' Option Strict is on.
Dim m As Long = 987
'Dim n As Integer = m
' The For Each loop requires the same conversion but
' causes no errors, even when Option Strict is on.
For Each number As Integer In New Long() {45, 3, 987}
Console.Write(number & " ")
Next
Console.WriteLine()
' Output: 45 3 987
' Here a run-time error is raised because 9876543210
' is too large for type Integer.
'For Each number As Integer In New Long() {45, 3, 9876543210}
' Console.Write(number & " ")
'Next
End Sub
End Module
IEnumerator 呼叫
For Each
...Next
迴圈執行開始時,Visual Basic 會 group
確認參考有效的集合物件。 如果沒有,則會擲回例外狀況。 否則,它會呼叫 MoveNext 方法及列舉值物件的 Current 屬性,以傳回第一個元素。 如果 MoveNext
表示沒有下一個元素,也就是說,如果集合是空的,For Each
迴圈就會停止和控制傳遞至 Next
陳述式後面的陳述式。 否則,Visual Basic 會將 element
設定為第一個元素,並執行 陳述式區塊。
每次 Visual Basic 遇到 Next
陳述式時,都會傳回 For Each
陳述式。 同樣地,它會呼叫 MoveNext
和 Current
並傳回下一個元素,並且再次執行區塊或根據結果停止迴圈。 此流程會繼續執行,直到 MoveNext
指出沒有下一個元素或遇到 Exit For
陳述式為止。
修改集合。 通常 GetEnumerator 傳回的列舉值物件不會讓您藉由新增、刪除、取代或重新排序任何元素來變更集合。 如果您在起始 For Each
...Next
迴圈之後變更集合,列舉值物件就會變成無效,而下一次嘗試存取元素會造成 InvalidOperationException 例外狀況。
不過,這項修改封鎖不是由 Visual Basic 決定,而是由 IEnumerable 介面的實作決定。 您可以透過允許在反覆項目期間修改的方式來實作 IEnumerable
。 如果您考慮進行這類動態修改,請確定您已瞭解所使用集合上的 IEnumerable
實作所呈現的特性。
修改集合元素。 列舉值物件的 Current 屬性是 ReadOnly,它會傳回每個集合元素的本機複本。 這表示您無法在 For Each
...Next
迴圈中修改元素本身。 您所做的任何修改只會影響來自 Current
的本機複本,而且不會反映回基礎集合中。 不過,如果元素是參考型別,您可以修改它所指向的執行個體成員。 下列範例會修改每個 thisControl
元素的 BackColor
成員。 不過,您無法修改 thisControl
本身。
Sub LightBlueBackground(thisForm As System.Windows.Forms.Form)
For Each thisControl In thisForm.Controls
thisControl.BackColor = System.Drawing.Color.LightBlue
Next thisControl
End Sub
上一個範例可以修改每個 thisControl
元素的 BackColor
成員,但無法修改 thisControl
本身。
周遊陣列。 因為 Array 類別會實作 IEnumerable 介面,所以所有陣列都會公開 GetEnumerator 方法。 這表示您可以使用 For Each
...Next
迴圈逐一查看陣列。 不過,您只能讀取陣列元素。 您無法變更這些元素。
範例 1
下列範例會使用 DirectoryInfo 類別列出 C:\ 目錄中的所有資料夾。
Dim dInfo As New System.IO.DirectoryInfo("c:\")
For Each dir As System.IO.DirectoryInfo In dInfo.GetDirectories()
Debug.WriteLine(dir.Name)
Next
範例 2
下列範例說明排序集合的程序。 此範例排序儲存在 List<T> 中的 Car
類別執行個體。 Car
類別實作 IComparable<T> 介面,而這個介面要求實作 CompareTo 方法。
每次對 CompareTo 方法的呼叫都會進行用於排序的單一比較。 當目前物件和另一個物件比較時,在 CompareTo
方法中的使用者撰寫程式碼會傳回值。 如果目前物件比另一個物件小則傳回的值小於零,如果目前物件比另一個物件大則傳回的值大於零,如果它們相等則傳回零。 這可讓您以程式碼定義大於、小於、等於的準則。
在 ListCars
方法中,cars.Sort()
陳述式會排序清單。 對 List<T> 之 Sort 方法的這個呼叫,會導致 CompareTo
方法對 List
的 Car
物件自動呼叫。
Public Sub ListCars()
' Create some new cars.
Dim cars As New List(Of Car) From
{
New Car With {.Name = "car1", .Color = "blue", .Speed = 20},
New Car With {.Name = "car2", .Color = "red", .Speed = 50},
New Car With {.Name = "car3", .Color = "green", .Speed = 10},
New Car With {.Name = "car4", .Color = "blue", .Speed = 50},
New Car With {.Name = "car5", .Color = "blue", .Speed = 30},
New Car With {.Name = "car6", .Color = "red", .Speed = 60},
New Car With {.Name = "car7", .Color = "green", .Speed = 50}
}
' Sort the cars by color alphabetically, and then by speed
' in descending order.
cars.Sort()
' View all of the cars.
For Each thisCar As Car In cars
Debug.Write(thisCar.Color.PadRight(5) & " ")
Debug.Write(thisCar.Speed.ToString & " ")
Debug.Write(thisCar.Name)
Debug.WriteLine("")
Next
' Output:
' blue 50 car4
' blue 30 car5
' blue 20 car1
' green 50 car7
' green 10 car3
' red 60 car6
' red 50 car2
End Sub
Public Class Car
Implements IComparable(Of Car)
Public Property Name As String
Public Property Speed As Integer
Public Property Color As String
Public Function CompareTo(ByVal other As Car) As Integer _
Implements System.IComparable(Of Car).CompareTo
' A call to this method makes a single comparison that is
' used for sorting.
' Determine the relative order of the objects being compared.
' Sort by color alphabetically, and then by speed in
' descending order.
' Compare the colors.
Dim compare As Integer
compare = String.Compare(Me.Color, other.Color, True)
' If the colors are the same, compare the speeds.
If compare = 0 Then
compare = Me.Speed.CompareTo(other.Speed)
' Use descending order for speed.
compare = -compare
End If
Return compare
End Function
End Class