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

如果尚未声明 element,则是必选项。 element 的数据类型。

group

必选。 对象变量。 引用要重复 statements 的集合。

statements

可选。 For Each 和 Next 之间的一条或多条语句,这些语句在 group 中的每一项上运行。

Continue For

可选。 将控制转移到 For Each 循环的开始。

Exit For

可选。 将控制转移到 For Each 循环外。

Next

必选。 终止 For Each 循环的定义。

备注

当需要为集合或数组的每个元素重复执行一组语句时,请使用 For Each...Next 循环。

Visual Basic 在循环开始之前只计算集合一次。 如果语句块更改 element 或 group,这些更改不影响循环的迭代。

在连续地将集合中的所有元素分配给 element 后,For Each 循环停止,并将控制权传递给 Next 语句后面的语句。

如果未在此循环外声明 element,则必须在 For Each 语句中声明它。 可以使用 As 语句显式声明 element 的类型,也可以依赖类型推断来分配该类型。 在任意一种情况下,element 的范围都是循环的主体。 但是,不能既在循环外又在循环内声明 element。

可以选择在 Next 语句中指定 element。 这将提高程序的可读性,尤其是在具有嵌套的 For Each 循环的情况下。 必须指定与相应的 For Each 语句中出现的变量相同的变量。

您可能希望避免在循环中更改 element 的值。 这样做可能会更难以阅读和调试代码。 更改 group 的值不会影响集合或其元素,它们已在首次进入循环时确定。

提示

当可以将循环的每次迭代与控制变量相关联并可确定该变量的初始值和最终值时,For...Next 语句 (Visual Basic) 非常适合。 但是,在处理集合时,初始值和最终值的概念没有意义,而且您不必知道集合中包含多少元素。 在这种情况下,For Each...Next 循环是一个更好的选择。

如果代码依赖于以特定顺序遍历集合,则 For Each...Next 循环不是最佳选择,除非您知道该集合公开的枚举数对象的特征。 遍历的顺序不是由 Visual Basic 确定的,而是由枚举数对象的 MoveNext 方法决定的。 因此,您可能无法预测 element 中首先返回集合中的哪个元素,或者在某个给定的元素后返回的下一个元素是哪个元素。 使用其他循环结构(如 For...Next 或 Do...Loop)可能会获得更可靠的结果.

嵌套循环

可以将一个循环放在另一个循环内以嵌套 For Each 循环。 不过,每个循环必须具有唯一的 element 变量。

您还可以将多个不同类型的控制结构相互进行嵌套。 有关更多信息,请参见 嵌套的控件结构 (Visual Basic)

如果先遇到外部嵌套级别的 Next 语句,后遇到内部嵌套级别的 Next 语句,编译器将发出错误信号。 不过,仅当在所有 Next 语句中都指定了 element 时,编译器才能检测到这种重叠错误。

退出 For

Exit For 语句致使执行退出 For…Next 循环,并将控制权转交给 Next 语句之后的语句。

可以在 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 语句 (Visual Basic)

Continue For 语句将控制权立即转移给下一轮循环。 有关更多信息,请参见 Continue 语句 (Visual Basic)

数据类型

element 的数据类型必须是 group 的元素的数据类型可以转换成的类型。

group 的数据类型必须是引用集合或可枚举的数组的引用类型。 通常情况下,这意味着 group 将引用实现 IEnumerable 命名空间 System.Collections 的接口或 IEnumerable<T> 命名空间 System.Collections.Generic 的接口。 System.Collections.IEnumerable 定义 GetEnumerator 方法,而该方法返回集合的枚举数对象。 枚举数对象实现 System.Collections 命名空间的 System.Collections.IEnumerator 接口,并公开 Current 属性以及 ResetMoveNext 方法。 Visual Basic 使用它们遍历集合。

group 的元素通常属于 Object 类型,但是可以拥有任何运行时数据类型。

收缩转换

当 Option Strict 设置为 On 时,收缩转换通常会导致编译器错误。 但是,在 For Each 中,会在运行时计算并执行从 group 中的元素到 element 的转换,并禁止显示收缩转换引起的编译器错误。

在下面的示例中,当 Option Strict 设置为 on 时,不会对将 m 赋为 n 的初始值的赋值语句进行编译,因为从 Long 到 Integer 的转换是收缩转换。 但是,在 For Each 语句中,没有报告编译器错误,即使对 number 的赋值同样需要由 Long 转换为 Integer。 在包含一个大数字的 For Each 语句中,在将 ToInteger 应用于该大数字时,出现运行时错误。

Option Strict On

Module Module1
    Sub Main()
        ' 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

        Console.ReadKey()
    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 语句。 它将再次调用 MoveNextCurrent 以返回下一个元素,然后根据结果再次运行块或者停止循环。 此过程将继续,直至 MoveNext 指示没有下一个元素或者遇到 Exit For 语句为止。

修改

**修改集合。**正常情况下,GetEnumerator 返回的枚举数对象不允许通过添加、删除、替换或重新排列任何元素来更改集合。 如果在启动 For Each...Next 循环后更改了集合,枚举数对象将失效,并且下次尝试访问元素时将引发 InvalidOperationException 异常。

但是,禁止修改不是由 Visual Basic 决定,而是由 IEnumerable 接口的具体实现方法决定。 在实现 IEnumerable 时可以允许在迭代期间进行修改。 如果要进行这种动态修改,请确保您对所用集合 IEnumerable 实现的特点有很好的理解。

**修改集合元素。**枚举数对象的 Current 属性为 ReadOnly (Visual Basic),它返回每个集合元素的本地副本。 这意味着不能在 For Each...Next 循环中修改元素本身。 您所做的任何修改只会影响从 Current 返回的本地副本,而不会反映回基础集合中。 但是,如果元素属于引用类型,则可以修改它所指向的实例的成员。 下面的示例阐释了这一点。

Sub lightBlueBackground(ByVal thisForm As System.Windows.Forms.Form)
    For Each thisControl As System.Windows.Forms.Control In thisForm.Controls
        thisControl.BackColor = System.Drawing.Color.LightBlue
    Next thisControl
End Sub

前面的示例可以修改每个 thisControl 元素的 BackColor 成员,但是不能修改 thisControl 自身。

**遍历数组。**由于 Array 类实现了 IEnumerable 接口,因此所有数组都公开 GetEnumerator 方法。 这意味着可以使用 For Each...Next 循环来循环访问数组。 但是,只能读取数组元素。 这些项无法更改。 有关阐述,请参见如何:为集合或数组中的每个元素运行多个语句 (Visual Basic)

示例

下面的示例显示如何使用 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…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 

下面的示例显示如何使用 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 7, 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

下面的示例使用 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

下面的示例显示如何通过使用 IComparable 接口对集合排序。

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
        ' Determine the relative order of the objects being compared.
        ' This is used to 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

下面的示例包含拥有自定义枚举器的自定义集合类。

Public Sub ListColors()
    Dim colors As New AllColors()

    For Each theColor As Color In colors
        Debug.Write(theColor.Name & " ")
    Next
    Debug.WriteLine("")
    ' Output: red blue green
End Sub

' Collection class.
Public Class AllColors
    Implements System.Collections.IEnumerable

    Private _colors() As Color =
    {
        New Color With {.Name = "red"},
        New Color With {.Name = "blue"},
        New Color With {.Name = "green"}
    }

    Public Function GetEnumerator() As System.Collections.IEnumerator _
        Implements System.Collections.IEnumerable.GetEnumerator

        Return New ColorEnumerator(_colors)

        ' Instead of creating a using a custom enumerator, you could
        ' use the GetEnumerator of the array.
        'Return _colors.GetEnumerator
    End Function

    ' Custom enumerator.
    Private Class ColorEnumerator
        Implements System.Collections.IEnumerator

        Private _colors() As Color
        Private _position As Integer = -1

        Public Sub New(ByVal colors() As Color)
            _colors = colors
        End Sub

        Public ReadOnly Property Current() As Object Implements System.Collections.IEnumerator.Current
            Get
                Return _colors(_position)
            End Get
        End Property

        Public Function MoveNext() As Boolean Implements System.Collections.IEnumerator.MoveNext
            _position += 1
            Return (_position < _colors.Length)
        End Function

        Public Sub Reset() Implements System.Collections.IEnumerator.Reset
            _position = -1
        End Sub
    End Class
End Class

' Element class.
Public Class Color
    Public Property Name As String
End Class

请参见

任务

如何:为集合或数组中的每个元素运行多个语句 (Visual Basic)

参考

For...Next 语句 (Visual Basic)

While...End While 语句 (Visual Basic)

Do...Loop 语句 (Visual Basic)

概念

循环结构 (Visual Basic)

Visual Basic 中的集合

扩大转换和收缩转换 (Visual Basic)

对象初始值设定项:命名类型和匿名类型 (Visual Basic)

集合初始值设定项概述 (Visual Basic)

数组 (Visual Basic)

修订记录

Date

修订记录

原因

2010 年 12 月

重组了“备注”部分,并添加了示例。

信息补充。