For Each...Next 陳述式 (Visual Basic)

針對集合中的每個元素重複一組語句。

Syntax

For Each element [ As datatype ] In group
    [ statements ]
    [ Continue For ]
    [ statements ]
    [ Exit For ]
    [ statements ]
Next [ element ]

組件

詞彙 定義
element 語句中的必要項 For Each 。 語句中的選擇性 Next 。 變數。 用來逐一查看集合的元素。
datatype 如果 Option Infer 是 on (預設) 或已宣告,則為選擇性 element ; 如果 Option Infer 為 off 且尚未宣告,則為必要項 elementelement 的資料類型。
group 必要。 型別為集合型別或物件的變數。 指 statements 要重複的集合。
statements 選擇性。 和之間的一個或多個語句 For Each Next ,這些語句會在中的每個專案上執行 group
Continue For 選擇性。 將控制權轉移到迴圈的起點 For Each
Exit For 選擇性。 將控制權移交給 For Each 迴圈。
Next 必要。 終止迴圈的定義 For Each

簡單範例

For Each Next 當您想要針對集合或陣列的每個元素重複一組語句時,請使用 ... loop。

提示

當您可以將迴圈的每個反復專案與控制項變數產生關聯,並判斷變數的初始和最終值時,下一個語句的效果很好。 不過,當您處理集合時,初始和最終值的概念不會有意義,而且您不一定知道集合具有多少元素。 在這種情況下,「 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 ForExit 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

您可以將任意數目的 Exit For 語句放在 For Each 迴圈中。 在嵌套迴圈中使用時 For EachExit For 會導致執行結束最內層的迴圈,並將控制權轉移至下一個較高層級的嵌套。

Exit For 通常用於評估某些條件之後,例如,在 If ... Then...Else 結構。 您可能會想要在 Exit For 下列情況下使用:

  • 繼續進行反覆運算是不必要或不可能的。 這可能是由錯誤值或終止要求所造成。

  • 在中 Try Catch 攔截到例外狀況...Finally.您可能會 Exit For 在區塊結尾處使用 Finally

  • 有無限迴圈,也就是可以執行大量甚至無限次數的迴圈。 如果您偵測到這種情況,您可以使用 Exit For 來 escape 迴圈。 如需詳細資訊, 請參閱 .。。迴圈語句

迭代器

您可以使用 反覆運算器 ,在集合上執行自訂反復專案。 反覆運算器可以是函數或存取子 Get 。 它會使用 Yield 語句,一次傳回一個集合的每個元素。

您可以使用語句來呼叫反覆運算器 For Each...NextFor Each 迴圈的每個反覆項目都會呼叫迭代器。 在 Yield 反覆運算器中到達語句時,會傳回語句中的運算式, Yield 並保留程式碼中的目前位置。 下一次呼叫迭代器時,便會從這個位置重新開始執行。

下列範例會使用 iterator 函數。 反覆運算器函式的 Yield 語句位於 For .。。Next 迴圈。 在 ListEvenNumbers 方法中,語句主體的每個反復專案 For Each 都會建立對 iterator 函數的呼叫,以繼續進行下一個 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

如需詳細資訊,請參閱 反覆運算器、 Yield 語句Iterator

技術實作

For Each .。。Next 語句會執行,Visual Basic 只會在迴圈開始之前評估集合一次。 如果您的語句區塊變更 elementgroup ,這些變更不會影響迴圈的反覆運算。

當集合中的所有專案都是連續指派給時 element ,迴圈就會 For Each 停止,並將控制權傳遞給語句後面的語句 Next

如果選項推斷為 on (預設值) ,則 Visual Basic 編譯器可以推斷的資料型別 element 。 如果它是關閉的,而且 element 尚未在迴圈外部宣告,您必須在語句中宣告它 For Each 。 若要明確宣告的資料類型 element ,請使用 As 子句。 除非專案的資料類型定義于 For Each ... Next 結構之外,否則其範圍為迴圈的主體。 請注意,您不能在 element 迴圈之外和內部宣告。

您可以選擇性地 element 在語句中指定 Next 。 這可改善程式的可讀性,特別是當您有嵌套的 For Each 迴圈時。 您必須指定與出現在對應語句中的變數相同的變數 For Each

您可能會想要避免變更 element 迴圈內的值。 這樣做可能會讓您更難讀取及偵測程式碼。 變更的值 group 並不會影響集合或其元素,這是在第一次進入迴圈時所決定。

當您要嵌套迴圈時,如果在 Next 內部層級之前遇到外部嵌套層級的語句 Next ,則編譯器會發出錯誤信號。 不過,只有當您 element 在每個語句中指定時,編譯器才能偵測此重迭的錯誤 Next

如果您的程式碼相依于依特定順序來遍歷集合,則 For Each 不是 Next 最好的選擇,除非您知道集合所公開之列舉值物件的特性。 遍歷的順序不是由 Visual Basic 所決定,而是由 MoveNext 列舉值物件的方法決定。 因此,您可能無法預測集合的哪個專案是要在中傳回的第一個元素 element ,或是在指定專案之後要傳回的下一個專案。 您可以使用不同的迴圈結構,例如 For ... NextDo ...,以達成更可靠的 Loop 結果。

執行時間必須能夠將中的元素轉換 groupelement 。 [ Option Strict ] 語句控制是否允許擴展和縮小轉換 (Option Strict 為關閉、其預設值) ,或) 上是否允許擴輾轉換 (Option Strict 。 如需詳細資訊,請參閱 縮小轉換

的資料類型 group 必須是參考型別,而參考型別是指可列舉的集合或陣列。 最常見的方式 group 是參考物件,該物件會執行 IEnumerable System.Collections 命名空間的介面或 IEnumerable<T> 命名空間的介面 System.Collections.GenericSystem.Collections.IEnumerable 定義 GetEnumerator 方法,此方法會傳回集合的列舉值物件。 列舉值物件會執行 System.Collections.IEnumerator System.Collections 命名空間的介面,並公開 Current 屬性和 ResetMoveNext 方法。 Visual Basic 會使用這些來跨越集合。

縮小轉換

Option Strict 設定為時 On ,縮小轉換通常會造成編譯器錯誤。 不過,在語句中, For Each 從中的專案到的轉換 group element 會在執行時間進行評估和執行,並隱藏縮小轉換所造成的編譯器錯誤。

在下列範例中, m n Option Strict 由於的轉換 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列舉值物件的屬性是唯讀的,它會傳回每個集合元素的本機複本。 這表示您不能在 For Each ... 迴圈中修改元素本身。 Next 您所做的任何修改都只會影響的本機複本 Current ,而不會反映回基礎集合。 但是,如果專案是參考型別,您可以修改它所指向之實例的成員。 下列範例會修改 BackColor 每個元素的 thisControl 成員。 不過,您無法修改 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

上述範例可以修改 BackColor 每個專案的成員 thisControl ,但它無法自行修改 thisControl

遍歷陣列。 由於類別會實 Array 作為 IEnumerable 介面,因此所有陣列都會公開 GetEnumerator 方法。 這表示您可以使用 For Each ... 迴圈逐一查看陣列。 Next 不過,您只能讀取陣列元素。 您無法變更它們。

範例 1

下列範例會列出 C:\ 中的所有資料夾使用類別的目錄 DirectoryInfo

Dim dInfo As New System.IO.DirectoryInfo("c:\")
For Each dir As System.IO.DirectoryInfo In dInfo.GetDirectories()
    Debug.WriteLine(dir.Name)
Next

範例 2

下列範例說明排序集合的程序。 此範例 Car 會排序儲存在中之類別的實例 List<T>Car 類別實作 IComparable<T> 介面,而這個介面要求實作 CompareTo 方法。

每次呼叫 CompareTo 方法都會進行排序所使用的單一比較。 當目前物件和另一個物件比較時,在 CompareTo 方法中的使用者撰寫程式碼會傳回值。 如果目前物件比另一個物件小則傳回的值小於零,如果目前物件比另一個物件大則傳回的值大於零,如果它們相等則傳回零。 這可讓您以程式碼定義大於、小於、等於的準則。

ListCars 方法中,cars.Sort() 陳述式會排序清單。 對 List<T>Sort 方法的這個呼叫,會導致 CompareTo 方法對 ListCar 物件自動呼叫。

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

另請參閱