次の方法で共有


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 必須。 コレクション型またはオブジェクト型の変数。 statementsを繰り返すコレクションを参照します。
statements 任意。 groupの各項目で実行されるFor EachNextの間の 1 つ以上のステートメント。
Continue For 任意。 For Each ループの先頭に制御を転送します。
Exit For 任意。 For Each ループから制御を転送します。
Next 必須。 For Each ループの定義を終了します。

簡単な例

コレクションまたは配列の各要素に対してステートメントのセットを繰り返す場合は、 For Each...Next ループを使用します。

ヒント

A For...次のステートメントは、ループの各反復を制御変数に関連付け、その変数の初期値と最終値を決定できる場合に適切に機能します。 ただし、コレクションを扱う場合、初期値と最終値の概念は意味がなく、コレクションに含まれる要素の数が必ずしもわかりません。 このような場合、多くの場合、 For Each...Next ループが適しています。

次の例では、 For Each... Next ステートメントは、List コレクションのすべての要素を反復処理します。

' 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 と 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 でキャッチされる...FinallyFinally ブロックの末尾にExit Forを使用できます。

  • 無限ループがあります。これは、多数または無限の回数実行できるループです。 このような条件を検出した場合は、 Exit For を使用してループをエスケープできます。 詳細については、Do...Loop ステートメントを参照してください。

反復子

反復子を使用して、コレクションに対してカスタムイテレーションを実行します。 反復子には、関数または Get アクセサーを指定できます。 Yield ステートメントを使用して、コレクションの各要素を一度に 1 つずつ返します。

For Each...Next ステートメントを使用して反復子を呼び出します。 For Each ループの各反復処理で反復子が呼び出されます。 反復子で Yield ステートメントに到達すると、 Yield ステートメント内の式が返され、コード内の現在の位置が保持されます。 次に反復子が呼び出されるときに、その場所から実行が再開されます。

次の例では、反復子関数を使用します。 反復子関数には、For... 内の Yield ステートメントがあります 。次の ループ。 ListEvenNumbers メソッドでは、For Each ステートメント本体の各反復処理によって反復子関数の呼び出しが作成され、次の 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 ステートメント、反復子」を参照 してください

技術的な実装

For Eachの場合... Next ステートメントを実行すると、ループが開始される前に、Visual Basic によってコレクションが 1 回だけ評価されます。 ステートメント ブロックが element または groupに変更された場合、これらの変更はループの反復には影響しません。

コレクション内のすべての要素が elementに連続して割り当てられると、 For Each ループは停止し、 Next ステートメントの後のステートメントに制御が渡されます。

オプション推論がオン (既定の設定) の場合、Visual Basic コンパイラはelementのデータ型を推論できます。 オフで、 element がループの外部で宣言されていない場合は、 For Each ステートメントで宣言する必要があります。 elementのデータ型を明示的に宣言するには、As句を使用します。 要素のデータ型が For Each...Next コンストラクトの外部で定義されていない限り、そのスコープはループの本体です。 ループの外側と内部の両方 element 宣言できないことに注意してください。

必要に応じて、Next ステートメントでelementを指定できます。 これにより、特に入れ子になった For Each ループがある場合に、プログラムの読みやすさが向上します。 対応する For Each ステートメントに表示される変数と同じ変数を指定する必要があります。

ループ内で element の値を変更しないようにすることができます。 これを行うと、コードの読み取りとデバッグがより困難になる可能性があります。 groupの値を変更しても、ループが最初に入力されたときに決定されたコレクションまたはその要素には影響しません。

ループを入れ子にすると、外側の入れ子レベルの Next ステートメントが内部レベルの Next の前に検出された場合、コンパイラはエラーを通知します。 ただし、コンパイラは、すべてのelementステートメントでNextを指定した場合にのみ、この重複するエラーを検出できます。

コードが特定の順序でコレクションを走査することに依存している場合、コレクションが公開する列挙子オブジェクトの特性がわかっている場合を除き、 For Each...Next ループは最適な選択ではありません。 トラバーサルの順序は Visual Basic ではなく、列挙子オブジェクトの MoveNext メソッドによって決まります。 そのため、コレクションのどの要素が最初に elementで返されるか、または指定された要素の後に返される次の要素であるかを予測できない場合があります。 For...NextDo...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 StrictOn に設定すると、通常、縮小変換によってコンパイラ エラーが発生します。 ただし、 For Each ステートメントでは、 group 内の要素から element への変換は実行時に評価および実行され、縮小変換によって発生するコンパイラ エラーは抑制されます。

次の例では、LongからIntegerへの変換は縮小変換であるため、nの初期値としてのmの割り当ては、Option Strictがオンのときにコンパイルされません。 ただし、 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 ループの実行が開始されると、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 であり、各コレクション要素のローカル コピーを返します。 つまり、 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 メソッドを呼び出すたびに、並べ替えに使用される 1 つの比較が行われます。 CompareTo メソッドのユーザー記述コードは、現在のオブジェクトと別のオブジェクトの比較ごとに値を返します。 現在のオブジェクトが他のオブジェクトより小さい場合、返される値は 0 未満、現在のオブジェクトが他のオブジェクトより大きい場合は 0 より大きく、等しい場合は 0 になります。 これにより、より大きい、より小さい、等しいという条件をコードで定義できます。

ListCars メソッドでは、cars.Sort() ステートメントによってリストが並べ替えられます。 このSortList<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

こちらも参照ください