For Each...Next 语句 (Visual Basic)
更新: 2008 年 7 月
对于集合中的每个元素重复一组语句。
For Each element [ As datatype ] In group
[ statements ]
[ Exit For ]
[ statements ]
Next [ element ]
各部分说明
element
在 For Each 语句中是必选项。在 Next 语句中是可选项。变量。用于循环访问集合的元素。datatype
如果尚未声明 element,则是必选项。element 的数据类型。group
必需。对象变量。引用要重复 statements 的集合。statements
可选。For Each 和 Next 之间的一条或多条语句,这些语句在 group 中的每一项上运行。Exit For
可选。将控制转移到 For Each 循环外。Next
必需。终止 For Each 循环的定义。
备注
当需要为集合或数组的每个元素重复执行一组语句时,请使用 For Each...Next 循环。
当可以将循环的每次迭代与控制变量相关联并可确定该变量的初始值和最终值时,For...Next 语句 (Visual Basic) 非常适合。但是,在处理集合时,初始值和最终值的概念没有意义,而且您不必知道集合中包含多少元素。在这种情况下,For Each...Next 循环是一个更好的选择。
规则
数据类型。group 的元素的数据类型必须可以转换为 element 的数据类型。
group 的数据类型必须是引用集合或数组的引用类型。这意味着 group 必须引用实现 System.Collections 命名空间的 IEnumerable 接口或 System.Collections.Generic 命名空间的 IEnumerable<T> 接口的对象。IEnumerable 定义 GetEnumerator 方法,而该方法返回集合的枚举数对象。枚举数对象实现 System.Collections 命名空间的 IEnumerator 接口,并公开 Current 属性以及 Reset 和 MoveNext 方法。Visual Basic 使用它们遍历集合。
group 的元素通常属于 Object 类型,但是可以拥有任何运行时数据类型。
**收缩转换。**当 Option Strict 设置为 On 时,收缩转换通常会导致编译器错误。在下面的示例中,当 Option Strict 设置为 on 时,不会对将 m 赋为 n 的初始值的赋值语句进行编译,因为从 Long 到 Integer 的转换是收缩转换。
Dim m As Long = 987 ' Does not compile. 'Dim n As Integer = m
但是,会在运行时计算并执行从 group 中的元素到 element 的转换,并禁止显示收缩转换错误。在下面的示例中,虽然要求使用从 Long 到 Integer 的相同转换(该转换在上一示例中导致了错误),也不会在 For Each 循环中报告任何编译器错误。
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. The output is 45 3 987. For Each p As Integer In New Long() {45, 3, 987} Console.Write(p & " ") Next Console.WriteLine() End Sub End Module
不出现编译器错误并不会消除存在运行时错误的风险。在下面的示例中,不会报告任何编译器错误,但是在将 ToInteger 应用于 9876543210 时会发生运行时错误。无论 Option Strict 是 on 还是 off,都会发生该运行时错误。
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 = 9876543210 'Dim n As Integer = m Try ' The For Each loop requires the same conversion, but ' is not flagged by the compiler. A run-time error is ' raised because 9876543210 is too large for type Integer. For Each p As Integer In New Long() {45, 3, 9876543210} Console.Write(p & " ") Next Console.WriteLine() Catch e As System.OverflowException Console.WriteLine() Console.WriteLine(e.Message) End Try End Sub End Module
**声明。**如果未在此循环外声明 element,则必须在 For Each 语句内声明它。可以使用 As 语句显式声明 element 的类型,也可以依赖类型推断来分配该类型。在任意一种情况下,element 的范围都是循环的主体。但是,不能既在循环外声明 element,又在循环内声明 element。
**迭代次数。**Visual Basic 在循环开始之前只计算集合一次。如果语句块更改 element 或 group,这些更改不影响循环的迭代。
**嵌套循环。**可以将一个循环放在另一个循环内以嵌套 For Each 循环。不过,每个循环必须具有唯一的 element 变量。
还可以在一种控制结构中嵌套其他种类的控制结构。有关更多信息,请参见嵌套的控制结构。
说明: 如果先遇到外部嵌套级别的 Next 语句,后遇到内部嵌套级别的 Next 语句,编译器将发出错误信号。不过,仅当在所有 Next 语句中都指定了 element 时,编译器才能检测到这种重叠错误。
**标识控制变量。**可以选择在 Next 语句中指定 element。这将提高程序的可读性,尤其是在具有嵌套的 For Each 循环的情况下。必须指定与相应的 For Each 语句中出现的变量相同的变量。
将控制转移到循环外。Exit 语句 (Visual Basic) 将控制立即转移到 Next 语句后面的语句。如果检测到使继续迭代不必要或不可能的条件(如错误值或终止请求),则可能需要退出循环。而且,如果在 Try...Catch...Finally 语句中捕获异常,则可以在 Finally 块的结尾使用 Exit For。
可以在 For Each 循环中的任何位置插入任意数量的 Exit For 语句。Exit For 通常在计算特定条件后使用,例如在 If...Then...Else 结构中。
**无限循环。**Exit For 的一种用途是测试可能导致“无限循环”(即运行次数非常多甚至无限的循环)的条件。如果检测到这样的条件,就可以使用 Exit For 退出循环。有关更多信息,请参见 Do...Loop 语句 (Visual Basic)。
行为
**进入循环。**开始执行 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 语句为止。
**循环的终止。**当连续地将集合中的所有元素分配给 element 后,For Each 循环终止,并将控制传递给 Next 语句后面的语句。
**更改迭代值。**如果在循环内更改 element 的值,将会使代码的阅读和调试变得更加困难。更改 group 的值不会影响集合或其元素,它们已在首次进入循环时确定。
**遍历顺序。**执行 For Each...Next 循环时,将在 GetEnumerator 方法返回的枚举数对象的控制下遍历集合。遍历的顺序不是由 Visual Basic 确定的,而是由枚举数对象的 MoveNext 方法决定的。这意味着您可能无法预测 element 中首先返回集合中的哪个元素,或者在某个给定的元素后返回的下一个元素是哪个元素。
如果代码依赖于以特定顺序遍历集合,则 For Each...Next 循环不是最佳选择,除非您知道该集合公开的枚举数对象的特征。使用其他循环结构(如 For...Next 或 Do...Loop)可能会获得更可靠的结果。
**修改集合。**正常情况下,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 循环来循环访问数组。但是,只能读取数组元素,而不能进行更改。有关阐释,请参见如何:为集合或数组中的每个元素运行多个语句。
示例
下面的示例使用 For Each...Next 语句在集合的所有元素中搜索字符串“Hello”。此示例假定已创建集合 thisCollection,并且其元素的类型为 String。
Dim found As Boolean = False
Dim thisCollection As New Collection
For Each thisObject As String In thisCollection
If thisObject = "Hello" Then
found = True
Exit For
End If
Next thisObject
请参见
任务
概念
参考
While...End While 语句 (Visual Basic)
修订记录
日期 |
修订记录 |
原因 |
---|---|---|
2008 年 7 月 |
增加了有关收缩转换的一节。 |
客户反馈。 |