For Each...Next 语句 (Visual Basic)For Each...Next Statement (Visual Basic)

为集合中的每个元素重复一组语句。Repeats a group of statements for each element in a collection.

语法Syntax

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

部件Parts

术语Term 定义Definition
element For Each 语句中是必需的。Required in the For Each statement. Next 语句中是可选的。Optional in the Next statement. 各种.Variable. 用于循环访问集合中的元素。Used to iterate through the elements of the collection.
datatype 如果Option Infer为 on (默认值)或已声明 element,则为可选;如果 @no__t 为 off,并且尚未声明-4 @no__t,则为必需。Optional if Option Infer is on (the default) or element is already declared; required if Option Infer is off and element isn't already declared. element的数据类型。The data type of element.
group 必需。Required. 类型为集合类型或对象的变量。A variable with a type that's a collection type or Object. 引用要重复 statements 的集合。Refers to the collection over which the statements are to be repeated.
statements 可选。Optional. group 中的每一项上运行 For EachNext 之间的一个或多个语句。One or more statements between For Each and Next that run on each item in group.
Continue For 可选。Optional. 将控制转移到 For Each 循环的开头。Transfers control to the start of the For Each loop.
Exit For 可选。Optional. 将控制转移到 For Each 循环中。Transfers control out of the For Each loop.
Next 必需。Required. 终止 For Each 循环的定义。Terminates the definition of the For Each loop.

简单示例Simple Example

若要为集合或数组的每个元素重复一组语句,请使用 For Each ... Next 循环。Use a For Each...Next loop when you want to repeat a set of statements for each element of a collection or array.

提示

用于 .。。如果可以将循环的每次迭代与控件变量相关联,并确定该变量的初始值,则下一语句会很好地运行。A For...Next Statement works well when you can associate each iteration of a loop with a control variable and determine that variable's initial and final values. 但是,在处理集合时,初始值和 final 值的概念没有意义,并且您不一定知道该集合具有多少元素。However, when you are dealing with a collection, the concept of initial and final values isn't meaningful, and you don't necessarily know how many elements the collection has. 在这种情况下,For Each ... @no__t 循环通常是更好的选择。In this kind of case, a For Each...Next loop is often a better choice.

在下面的示例中,@no__t NextIn the following example, the For EachNext 语句循环访问列表集合的所有元素。statement iterates through all the elements of a List collection.

' 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 more examples, see Collections and Arrays.

嵌套循环Nested Loops

可以通过在另一个循环中放置一个循环来嵌套 @no__t 0 个循环。You can nest For Each loops by putting one loop within another.

下面的示例演示了嵌套 For Each ... NextThe following example demonstrates nested For EachNext 构造.structures.

' 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 

嵌套循环时,每个循环必须具有唯一的 @no__t 0 变量。When you nest loops, each loop must have a unique element variable.

您还可以在彼此之间嵌套不同种类的控制结构。You can also nest different kinds of control structures within each other. 有关详细信息,请参阅嵌套控制结构For more information, see Nested Control Structures.

退出并继续进行Exit For and Continue For

Exit For语句导致执行退出 For ... NextThe Exit For statement causes execution to exit the ForNext 循环并将控制转移到 Next 语句后面的语句。loop and transfers control to the statement that follows the Next statement.

@No__t-0 语句会立即将控制转移到循环的下一次迭代。The Continue For statement transfers control immediately to the next iteration of the loop. 有关详细信息,请参阅Continue 语句For more information, see Continue Statement.

下面的示例演示如何使用 Continue ForExit For 语句。The following example shows how to use the Continue For and Exit For statements.

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

可以将任意数量的 Exit For 语句置于 @no__t 1 循环中。You can put any number of Exit For statements in a For Each loop. 在嵌套 For Each 循环内使用时,Exit For 将导致执行退出最内层的循环,并将控制转移到下一个更高的嵌套级别。When used within nested For Each loops, Exit For causes execution to exit the innermost loop and transfers control to the next higher level of nesting.

Exit For 在计算某些条件之后(例如,在 If ... Then ... @no__t 结构中),通常使用。Exit For is often used after an evaluation of some condition, for example, in an If...Then...Else structure. 在以下条件下,你可能想要使用 Exit ForYou might want to use Exit For for the following conditions:

  • 不需要继续循环访问。Continuing to iterate is unnecessary or impossible. 这可能是由错误值或终止请求引起的。This might be caused by an erroneous value or a termination request.

  • Try ... Catch ... Finally 中捕获到异常。你可能会在 @no__t 块的末尾使用 Exit ForAn exception is caught in a Try...Catch...Finally. You might use Exit For at the end of the Finally block.

  • 有一个无限循环,该循环是一种循环,它可以运行很大甚至无限次。There an endless loop, which is a loop that could run a large or even infinite number of times. 如果检测到这种情况,可以使用 Exit For 来转义循环。If you detect such a condition, you can use Exit For to escape the loop. 有关详细信息,请参阅Do...Loop 语句For more information, see Do...Loop Statement.

IteratorsIterators

使用迭代器对集合执行自定义迭代。You use an iterator to perform a custom iteration over a collection. 迭代器可以是函数或 Get 取值函数。An iterator can be a function or a Get accessor. 它使用 Yield 语句每次返回集合中的每个元素。It uses a Yield statement to return each element of the collection one at a time.

通过使用 For Each...Next 语句来调用迭代器。You call an iterator by using a For Each...Next statement. For Each 循环的每次迭代都会调用迭代器。Each iteration of the For Each loop calls the iterator. 在迭代器中到达 Yield 语句时,将返回 @no__t 为1的语句中的表达式,并保留当前在代码中的位置。When a Yield statement is reached in the iterator, the expression in the Yield statement is returned, and the current location in code is retained. 下次调用迭代器时,将从该位置重新开始执行。Execution is restarted from that location the next time that the iterator is called.

下面的示例使用迭代器函数。The following example uses an iterator function. 迭代器函数有一个 Yield 语句,该语句位于For...Next 语句循环。The iterator function has a Yield statement that's inside a For…Next loop. ListEvenNumbers 方法中,第一次 @no__t 语句体的每次迭代都会创建对迭代器函数的调用,该函数将继续执行下一个 @no__t 第2条语句。In the ListEvenNumbers method, each iteration of the For Each statement body creates a call to the iterator function, which proceeds to the next Yield statement.

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 语句迭代器For more information, see Iterators, Yield Statement, and Iterator.

技术实现Technical Implementation

如果 For Each ... NextWhen a For EachNext 语句运行时,Visual Basic 在循环开始之前仅计算一次集合。statement runs, Visual Basic evaluates the collection only one time, before the loop starts. 如果语句块更改 elementgroup,则这些更改不会影响循环的迭代。If your statement block changes element or group, these changes don't affect the iteration of the loop.

当集合中的所有元素连续分配到 element 时,@no__t 循环停止,并控制传递到 Next 语句后面的语句。When all the elements in the collection have been successively assigned to element, the For Each loop stops and control passes to the statement following the Next statement.

如果选项推断为 on (其默认设置),则 Visual Basic 编译器可以推断 @no__t 的数据类型。If Option Infer is on (its default setting), the Visual Basic compiler can infer the data type of element. 如果它处于关闭状态,并且未在循环外声明 element,则必须在 @no__t 1 语句中声明它。If it is off and element hasn't been declared outside the loop, you must declare it in the For Each statement. 若要显式声明 @no__t 0 的数据类型,请使用 @no__t 子句。To declare the data type of element explicitly, use an As clause. 除非在 For Each ... Next 构造之外定义元素的数据类型,否则其作用域为循环的主体。Unless the data type of element is defined outside the For Each...Next construct, its scope is the body of the loop. 请注意,不能在循环的外部和内部声明 @no__t 0。Note that you cannot declare element both outside and inside the loop.

您可以选择在 @no__t 语句中指定 elementYou can optionally specify element in the Next statement. 这会提高程序的可读性,尤其是在嵌套 @no__t 0 循环的情况下。This improves the readability of your program, especially if you have nested For Each loops. 您必须指定与在相应 For Each 语句中显示的变量相同的变量。You must specify the same variable as the one that appears in the corresponding For Each statement.

你可能希望避免在循环内更改 element 的值。You might want to avoid changing the value of element inside a loop. 这样做可以更难读取和调试代码。Doing this can make it more difficult to read and debug your code. 更改 group 的值不会影响集合或其元素,这是在第一次进入循环时确定的。Changing the value of group doesn't affect the collection or its elements, which were determined when the loop was first entered.

嵌套循环时,如果在内部级别的 @no__t 之前遇到外部嵌套级别的 @no__t 0 语句,则编译器会发出错误消息。When you're nesting loops, if a Next statement of an outer nesting level is encountered before the Next of an inner level, the compiler signals an error. 但是,只有在每个 @no__t 语句中指定 @no__t 0 时,编译器才能检测到此重叠错误。However, the compiler can detect this overlapping error only if you specify element in every Next statement.

如果你的代码依赖于遍历特定顺序的集合,则 For Each ... Next 循环不是最佳选择,除非你知道该集合公开的枚举器对象的特征。If your code depends on traversing a collection in a particular order, a For Each...Next loop isn't the best choice, unless you know the characteristics of the enumerator object the collection exposes. 遍历的顺序并不由 Visual Basic 确定,而是由枚举器对象的 @no__t 0 方法决定。The order of traversal isn't determined by Visual Basic, but by the MoveNext method of the enumerator object. 因此,你可能无法预测集合的哪个元素是在 element 中返回的第一个元素,或者是在给定元素后返回的下一个元素。Therefore, you might not be able to predict which element of the collection is the first to be returned in element, or which is the next to be returned after a given element. 你可能会使用不同的循环结构(例如 For ... NextDo ... @no__t)获得更可靠的结果。You might achieve more reliable results using a different loop structure, such as For...Next or Do...Loop.

运行时必须能够将 group 中的元素转换为 elementThe runtime must be able to convert the elements in group to element. [@No__t-0] 语句控制是否允许扩大转换和收缩转换(Option Strict 已关闭、其默认值),或者是否只允许扩大转换(Option Strict 为 on)。The [Option Strict] statement controls whether both widening and narrowing conversions are allowed (Option Strict is off, its default value), or whether only widening conversions are allowed (Option Strict is on). 有关详细信息,请参阅收缩转换For more information, see Narrowing conversions.

@No__t 的数据类型必须为引用类型,该引用类型引用可枚举的集合或数组。The data type of group must be a reference type that refers to a collection or an array that's enumerable. 大多数情况下,这意味着 group 指的是实现 System.Collections 命名空间的 @no__t 或 @no__t 的命名空间的 @no__t 接口的对象。Most commonly this means that group refers to an object that implements the IEnumerable interface of the System.Collections namespace or the IEnumerable<T> interface of the System.Collections.Generic namespace. @no__t 定义了 GetEnumerator 方法,该方法返回集合的枚举器对象。System.Collections.IEnumerable defines the GetEnumerator method, which returns an enumerator object for the collection. 枚举器对象实现了 @no__t 命名空间的 @no__t 0 接口,并公开了 @no__t @no__t 和 @no__t 方法。The enumerator object implements the System.Collections.IEnumerator interface of the System.Collections namespace and exposes the Current property and the Reset and MoveNext methods. Visual Basic 使用它们遍历集合。Visual Basic uses these to traverse the collection.

收缩转换Narrowing Conversions

如果 @no__t 设置为 On,则收缩转换通常会导致编译器错误。When Option Strict is set to On, narrowing conversions ordinarily cause compiler errors. 但是,在 For Each 语句中,将在运行时计算和执行从 groupelement 的元素的转换,并取消收缩转换导致的编译器错误。In a For Each statement, however, conversions from the elements in group to element are evaluated and performed at run time, and compiler errors caused by narrowing conversions are suppressed.

在下面的示例中,当 Option Strict 为 on 时,将 @no__t-@no__t 0 的赋值不会进行编译,因为 @no__t 3 到 @no__t 的转换是收缩转换。In the following example, the assignment of m as the initial value for n doesn't compile when Option Strict is on because the conversion of a Long to an Integer is a narrowing conversion. 但在 For Each 语句中,即使对 @no__t 的赋值要求从 LongInteger 的转换,也不会报告编译器错误。In the For Each statement, however, no compiler error is reported, even though the assignment to number requires the same conversion from Long to Integer. 在包含大量的 For Each 语句中,将 ToInteger 应用于大数值时,会发生运行时错误。In the For Each statement that contains a large number, a run-time error occurs when ToInteger is applied to the large number.

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 调用IEnumerator Calls

当执行 For Each ... @no__t 循环开始时,Visual Basic 将验证 group 是否引用了有效的集合对象。When execution of a For Each...Next loop starts, Visual Basic verifies that group refers to a valid collection object. 如果不是,则会引发异常。If not, it throws an exception. 否则,它会调用 MoveNext 方法和枚举器对象的 @no__t 属性,以返回第一个元素。Otherwise, it calls the MoveNext method and the Current property of the enumerator object to return the first element. 如果 MoveNext 指示没有下一个元素(即,如果集合为空),则 @no__t 循环停止,并控制将传递到 Next 语句后面的语句。If MoveNext indicates that there is no next element, that is, if the collection is empty, the For Each loop stops and control passes to the statement following the Next statement. 否则,Visual Basic 将 @no__t 0 设置为第一个元素,并运行语句块。Otherwise, Visual Basic sets element to the first element and runs the statement block.

每次 Visual Basic 遇到 @no__t 的语句时,它都会返回到 @no__t 语句。Each time Visual Basic encounters the Next statement, it returns to the For Each statement. 再次调用 MoveNextCurrent 返回下一个元素,并再次运行该块或停止循环,具体取决于结果。Again it calls MoveNext and Current to return the next element, and again it either runs the block or stops the loop depending on the result. 此过程将一直持续到 MoveNext 指示没有下一个元素或遇到 @no__t 的语句时。This process continues until MoveNext indicates that there is no next element or an Exit For statement is encountered.

修改集合。Modifying the Collection. @No__t-0 返回的枚举器对象通常不允许通过添加、删除、替换或重新排序任何元素来更改集合。The enumerator object returned by GetEnumerator normally doesn't let you change the collection by adding, deleting, replacing, or reordering any elements. 如果在启动 For Each ... Next 循环后更改集合,则枚举器对象将变为无效,并且下一次尝试访问元素时将导致 @no__t 2 异常。If you change the collection after you have initiated a For Each...Next loop, the enumerator object becomes invalid, and the next attempt to access an element causes an InvalidOperationException exception.

但是,这种修改阻塞不是由 Visual Basic 确定的,而是由实现 @no__t 的接口来决定的。However, this blocking of modification isn't determined by Visual Basic, but rather by the implementation of the IEnumerable interface. 可以通过允许在迭代期间进行修改的方式实现 IEnumerableIt is possible to implement IEnumerable in a way that allows for modification during iteration. 如果你正在考虑执行此类动态修改,请确保了解你所使用的集合上的 IEnumerable 实现的特征。If you are considering doing such dynamic modification, make sure that you understand the characteristics of the IEnumerable implementation on the collection you are using.

修改集合元素。Modifying Collection Elements. 枚举器对象的 Current 属性是只读的,它返回每个集合元素的本地副本。The Current property of the enumerator object is ReadOnly, and it returns a local copy of each collection element. 这意味着不能在 For Each ... Next 循环中修改元素本身。This means that you cannot modify the elements themselves in a For Each...Next loop. 您所做的任何修改只会影响 @no__t 的本地副本,而不会反映到基础集合。Any modification you make affects only the local copy from Current and isn't reflected back into the underlying collection. 但是,如果元素是引用类型,则可以修改它所指向的实例的成员。However, if an element is a reference type, you can modify the members of the instance to which it points. 下面的示例修改每个 @no__t 元素的 @no__t 0 个成员。The following example modifies the BackColor member of each thisControl element. 但不能修改 thisControl 本身。You cannot, however, modify thisControl itself.

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

前面的示例可以修改每个 @no__t 元素的 @no__t 0 个成员,不过它不能修改 thisControl 本身。The previous example can modify the BackColor member of each thisControl element, although it cannot modify thisControl itself.

遍历数组。Traversing Arrays. 由于 @no__t 0 类实现了 IEnumerable 接口,因此所有数组都公开了 GetEnumerator 方法。Because the Array class implements the IEnumerable interface, all arrays expose the GetEnumerator method. 这意味着,可以使用 For Each ... @no__t 循环循环访问数组。This means that you can iterate through an array with a For Each...Next loop. 但是,只能读取数组元素。However, you can only read the array elements. 不能更改它们。You cannot change them.

示例Example

下面的示例列出了 C:\ 中的所有文件夹使用 DirectoryInfo 类的目录。The following example lists all the folders in the C:\ directory by using the DirectoryInfo class.

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

示例Example

以下示例阐释了对集合排序的过程。The following example illustrates a procedure for sorting a collection. 该示例对存储在 @no__t 中的 @no__t 0 类的实例进行排序。The example sorts instances of a Car class that are stored in a List<T>. Car 类实现 IComparable<T> 接口,此操作需要实现 CompareTo 方法。The Car class implements the IComparable<T> interface, which requires that the CompareTo method be implemented.

每次调用 CompareTo 方法会进行排序时使用的单个比较。Each call to the CompareTo method makes a single comparison that's used for sorting. CompareTo 方法中用户编写的代码针对当前对象与另一个对象的每个比较返回一个值。User-written code in the CompareTo method returns a value for each comparison of the current object with another object. 如果当前对象小于另一个对象,则返回的值小于零;如果当前对象大于另一个对象,则返回的值大于零;如果当前对象等于另一个对象,则返回的值等于零。The value returned is less than zero if the current object is less than the other object, greater than zero if the current object is greater than the other object, and zero if they are equal. 这使你可以在代码中定义大于、小于和等于条件。This enables you to define in code the criteria for greater than, less than, and equal.

ListCars 方法中,cars.Sort() 语句对列表进行排序。In the ListCars method, the cars.Sort() statement sorts the list. List<T>Sort 方法的此调用将导致为 List 中的 Car 对象自动调用 CompareTo 方法。This call to the Sort method of the List<T> causes the CompareTo method to be called automatically for the Car objects in the List.

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

请参阅See also