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 までの間に記述した 1 つ以上のステートメントは、group 内の各項目に対して実行されます。

  • Exit For
    省略可能です。プログラムの制御を For Each ループの外に移します。

  • Next
    必ず指定します。For Each ループの定義を終了します。

解説

コレクションまたは配列の各要素に対して一連のステートメントを繰り返し実行するときには、For Each...Next ループを使用します。

For...Next ステートメント (Visual Basic) は、ループの毎回の反復を制御変数に関連付けることができ、制御変数の初期値と最終値を決定できるときには役立ちます。しかし、コレクションを扱うときには初期値と最終値の概念は意味を持ちませんし、コレクション内の要素の個数が必ずしも判明しているわけではありません。このような場合には、For Each...Next ループの方が適しています。

規則

  • データ型element のデータ型は、group の要素のデータ型から変換可能なものにする必要があります。

    group のデータ型は、コレクションまたは配列を参照する参照型にする必要があります。つまり、group は System.Collections 名前空間の IEnumerable インターフェイスまたは System.Collections.Generic 名前空間の IEnumerable<T> インターフェイスを実装するオブジェクトを参照する必要があります。IEnumerable は、コレクションの列挙子オブジェクトを返す GetEnumerator メソッドを定義します。列挙子オブジェクトは System.Collections 名前空間の IEnumerator インターフェイスを実装し、Current プロパティと Reset メソッドおよび MoveNext メソッドを公開します。このプロパティとメソッドは、コレクションの反復処理に使用されます。

    group の要素は、通常は Object 型にしますが、任意のランタイム データ型を指定できます。

  • **縮小変換。**Option Strict が On に設定されている場合、縮小変換は通常はコンパイラ エラーの原因になります。次の例では、Long の Integer への変換が縮小変換であるため、n の初期値としての m の代入は、Option Strict をオンにしてコンパイルできません。

    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 がオンまたはオフのいずれの場合も発生します。

    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 を宣言することはできません。

  • 繰り返しの回数 Visual Basic は、ループの開始前にコレクションを 1 回だけ評価します。ステートメント ブロックによって 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 ステートメントを好きな場所に何回でも記述できます。通常は、何らかの条件を評価した後、たとえば If...Then...Else 構造の後に Exit For を記述します。

  • 無限ループExit For の 1 つの用途は、無限ループ (実行回数が極端に多いループ、または無限に繰り返されるループ) を引き起こす可能性がある条件をテストすることです。このような条件を検出した場合は、Exit For を使用してループを抜けることができます。詳細については、「Do...Loop ステートメント (Visual Basic)」を参照してください。

動作

  • ループの開始For Each...Next ループの実行を開始するときには、group が有効なコレクション オブジェクトを参照しているかどうかが検証されます。有効なコレクション オブジェクトでない場合は、例外がスローされます。有効なコレクション オブジェクトである場合は、列挙子オブジェクトの MoveNext メソッドと Current プロパティが呼び出され、1 つ目の要素が返されます。MoveNext メソッドが次の要素がないことを示した場合、つまりコレクションが空の場合は、For Each ループは終了し、Next ステートメントの次のステートメントに制御が渡されます。それ以外の場合は、element が 1 つ目の要素に設定され、ステートメント ブロックが実行されます。

  • ループの反復Next ステートメントに達すると、そのたびに For Each ステートメントに制御が戻されます。次の要素を返すために再び MoveNextCurrent が呼び出され、その結果に応じて、ステートメント ブロックが再度実行されるか、ループが終了します。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

参照

処理手順

方法 : コレクションまたは配列で、各要素の複数のステートメントを実行する

方法 : ループのパフォーマンスを改善する

概念

ループ構造

Visual Basic におけるコレクション

拡大変換と縮小変換

参照

While...End While ステートメント (Visual Basic)

Do...Loop ステートメント (Visual Basic)

履歴の変更

日付

履歴

理由

2008 年 7 月

縮小変換に関するセクションを追加。

カスタマ フィードバック