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 |
必須です。 コレクション型または Object である型を持つ変数。 statements が繰り返されるコレクションを参照します。 |
statements |
任意。 group の各項目に対して実行される、For Each と Next の間の 1 つ以上のステートメント。 |
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
その他の例については、コレクションおよび配列に関するページを参照してください。
Nested Loops
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 と Continue For
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 ステートメント」を参照してください。
Iterators
反復子は、コレクションに対するカスタムの反復処理を実行するために使用します。 反復子は、関数または Get
アクセサーのいずれかです。 これは、Yield
ステートメントを使用して、コレクションの各要素を 1 回に 1 つ返します。
For Each...Next
ステートメントを使用して、反復子を呼び出します。 For Each
ループの各イテレーションは、反復子を呼び出します。 Yield
ステートメントが反復子に到達すると、Yield
ステートメント内の式が返され、コードの現在の位置が保持されます。 次回、反復子が呼び出されると、この位置から実行が再開されます。
次の例は、iterator 関数を使用します。 iterator 関数には、For…Next ループ内に Yield
ステートメントがあります。 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 によってコレクションが 1 回だけ評価されます。 ステートメント ブロックで 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
への変換は実行時に評価されて実行され、縮小変換によって発生するコンパイラ エラーは抑制されます。
次の例では、n
の初期値としての m
の割り当ては、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 プロパティは 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()
ステートメントがリストを並べ替えます。 Sort の List<T> メソッドへの呼び出しによって、CompareTo
メソッドは Car
内の 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
関連項目
.NET