次の方法で共有


ステートメント - Visual Basic

ステートメントは実行可能コードを表します。

Statement
    : LabelDeclarationStatement
    | LocalDeclarationStatement
    | WithStatement
    | SyncLockStatement
    | EventStatement
    | AssignmentStatement
    | InvocationStatement
    | ConditionalStatement
    | LoopStatement
    | ErrorHandlingStatement
    | BranchStatement
    | ArrayHandlingStatement
    | UsingStatement
	| AwaitStatement
	| YieldStatement
    ;

"注: Microsoft Visual Basic コンパイラでは、キーワードまたは識別子で始まるステートメントのみが許可されます。 したがって、たとえば、呼び出しステートメント "Call (Console).WriteLine" は許可されますが、呼び出しステートメント "(Console).WriteLine" は許可されません。

制御フロー

制御フロー は、ステートメントと式が実行されるシーケンスです。 実行の順序は、特定のステートメントまたは式によって異なります。

たとえば、加算演算子 (セクション 加算演算子) を評価する場合、最初に左オペランドが評価され、次に右オペランド、次に演算子自体が評価されます。 最初にサブステートメントを実行 してから、ブロックのステートメントを 1 つずつ進めることで、ブロックが実行されます (セクション ブロックとラベル)。

この順序付けの暗黙的な概念は、次に実行される操作である コントロール ポイントの概念です。 メソッドが呼び出されると (または "呼び出されました")、メソッドの インスタンス が作成されると言います。 メソッド インスタンスは、メソッドのパラメーターとローカル変数の独自のコピーと、独自のコントロール ポイントで構成されます。

標準メソッド

通常のメソッドの例を次に示します。

Function Test() As Integer
    Console.WriteLine("hello")
    Return 1
End Function

Dim x = Test()    ' invokes the function, prints "hello", assigns 1 to x

通常のメソッドが呼び出されると、

  1. まず、その呼び出しに固有のメソッドのインスタンスが作成されます。 このインスタンスには、メソッドのすべてのパラメーターとローカル変数のコピーが含まれています。
  2. その後、すべてのパラメーターが指定された値に初期化され、そのすべてのローカル変数が型の既定値に初期化されます。
  3. Functionの場合、暗黙的なローカル変数は、関数の戻り値変数とも呼ばれ、その名前は関数の名前で、型は関数の戻り値の型であり、初期値はその型の既定値です。
  4. その後、メソッド インスタンスの制御ポイントがメソッド本体の最初のステートメントに設定され、メソッド本体はそこからすぐに実行を開始します (Section Blocks と Labels)。

制御フローがメソッド本体を正常に終了する場合 (末尾を示す End Function または End Sub に到達するか、明示的な Return または Exit ステートメントを通じて) 制御フローがメソッド インスタンスの呼び出し元に戻ります。 関数の戻り値変数がある場合、呼び出しの結果はこの変数の最終的な値になります。

制御フローがハンドルされない例外を介してメソッド本体を終了すると、その例外が呼び出し元に伝達されます。

制御フローが終了すると、メソッド インスタンスへのライブ参照はなくなります。 メソッド インスタンスがローカル変数またはパラメーターのコピーへの唯一の参照を保持している場合は、ガベージ コレクションされる可能性があります。

Iterator メソッド

反復子メソッドは、 For Each ステートメントで使用できるシーケンスを生成する便利な方法として使用されます。 反復子メソッドは、 Yield ステートメント (Section Yield ステートメント) を使用してシーケンスの要素を提供します。 ( Yield ステートメントのない反復子メソッドは、空のシーケンスを生成します)。 反復子メソッドの例を次に示します。

Iterator Function Test() As IEnumerable(Of Integer)
    Console.WriteLine("hello")
    Yield 1
    Yield 2
End Function

Dim en = Test()
For Each x In en          ' prints "hello" before the first x
    Console.WriteLine(x)  ' prints "1" and then "2"
Next

戻り値の型が IEnumerator(Of T)されている反復子メソッドが呼び出されると、

  1. まず、反復子メソッドのインスタンスがその呼び出しに固有に作成されます。 このインスタンスには、メソッドのすべてのパラメーターとローカル変数のコピーが含まれています。
  2. その後、すべてのパラメーターが指定された値に初期化され、そのすべてのローカル変数が型の既定値に初期化されます。
  3. 暗黙的なローカル変数は、 反復子の現在の変数とも呼ばれ、その型が T され、その初期値がその型の既定値である初期化されます。
  4. メソッド インスタンスの制御ポイントは、メソッド本体の最初のステートメントで設定されます。
  5. その後、反復子 オブジェクト が作成され、このメソッド インスタンスに関連付けられます。 反復子オブジェクトは、宣言された戻り値の型を実装し、以下で説明するように動作します。
  6. その後、呼び出し元で 制御フローが直ちに 再開され、呼び出しの結果が反復子オブジェクトになります。 この転送は反復子メソッド インスタンスを終了せずに実行され、最終的にハンドラーが実行されることはありません。 メソッド インスタンスは反復子オブジェクトによって引き続き参照され、反復子オブジェクトへのライブ参照が存在する限りガベージ コレクションされません。

反復子オブジェクトの Current プロパティにアクセスすると、呼び出しの 現在の変数 が返されます。

反復子オブジェクトの MoveNext メソッドが呼び出されると、呼び出しによって新しいメソッド インスタンスは作成されません。 代わりに、既存のメソッド インスタンス (およびそのコントロール ポイントとローカル変数とパラメーター) が使用されます。反復子メソッドが最初に呼び出されたときに作成されたインスタンスです。 制御フローは、そのメソッド インスタンスの制御ポイントで実行を再開し、反復子メソッドの本体を通常どおりに続行します。

反復子オブジェクトの Dispose メソッドが呼び出されると、もう一度既存のメソッド インスタンスが使用されます。 制御フローは、そのメソッド インスタンスの制御ポイントで再開されますが、 Exit Function ステートメントが次の操作であるかのように直ちに動作します。

反復子オブジェクトでの MoveNext または Dispose の呼び出しに関する上記の動作の説明は、その反復子オブジェクトに対する以前の MoveNext または Dispose の呼び出しがすべて既に呼び出し元に戻っている場合にのみ適用されます。 そうでない場合、動作は未定義です。

制御フローが反復子メソッド本体を正常に終了する場合 (末尾をマークする End Function に到達する場合、または明示的な Return または Exit ステートメントを介して終了する場合は、反復子オブジェクトの MoveNext または Dispose 関数の呼び出しのコンテキストで実行して反復子メソッド インスタンスを再開する必要があり、反復子メソッドが最初に呼び出されたときに作成されたメソッド インスタンスを使用している必要があります。 そのインスタンスの制御ポイントは End Function ステートメントに残り、制御フローは呼び出し元で再開されます。 MoveNext の呼び出しによって再開された場合は、 False 値が呼び出し元に返されます。

制御フローがハンドルされない例外を通じて反復子メソッド本体を終了すると、例外が呼び出し元に伝達されます。呼び出し元は、再び MoveNext の呼び出しまたは Disposeの呼び出しになります。

反復子関数の他の可能な戻り値の型については、

  • 戻り値の型が一部のTに対してIEnumerable(Of T)されている反復子メソッドが呼び出されると、インスタンスが最初に作成されます。これは、反復子メソッドの呼び出しに固有であり、メソッド内のすべてのパラメーターに固有であり、指定された値で初期化されます。 呼び出しの結果は、戻り値の型を実装するオブジェクトです。 このオブジェクトの GetEnumerator メソッドを呼び出した場合、メソッド内のすべてのパラメーターとローカル変数のインスタンス ( GetEnumerator の呼び出しに固有) が作成されます。 これは、既に保存されている値に対してパラメーターを初期化し、上記の反復子メソッドと同様に続行します。
  • 戻り値の型が非ジェネリック インターフェイス IEnumeratorである反復子メソッドが呼び出されると、動作は IEnumerator(Of Object)とまったく同じになります。
  • 戻り値の型が非ジェネリック インターフェイス IEnumerableである反復子メソッドが呼び出されると、動作は IEnumerable(Of Object)とまったく同じになります。

非同期メソッド

非同期メソッドは、アプリケーションの UI をブロックすることなく、実行時間の長い作業を行う便利な方法です。 非同期は 非同期 の略です。つまり、非同期メソッドの呼び出し元は、すぐに実行を再開しますが、非同期メソッドのインスタンスの最終的な完了は後で発生する可能性があります。 慣例により、非同期メソッドはサフィックス "Async" で名前が付けられます。

Async Function TestAsync() As Task(Of String)
    Console.WriteLine("hello")
    Await Task.Delay(100)
    Return "world"
End Function

Dim t = TestAsync()         ' prints "hello"
Console.WriteLine(Await t)  ' prints "world"

"注: 非同期メソッドは、バックグラウンド スレッドでは実行 されません 。 代わりに、メソッドが Await 演算子を通じて自身を中断し、何らかのイベントに応答して再開されるようにスケジュールすることができます。

非同期メソッドが呼び出されたとき

  1. まず、その呼び出しに固有の非同期メソッドのインスタンスが作成されます。 このインスタンスには、メソッドのすべてのパラメーターとローカル変数のコピーが含まれています。
  2. その後、すべてのパラメーターが指定された値に初期化され、そのすべてのローカル変数が型の既定値に初期化されます。
  3. 一部のTの戻り値の型がTask(Of T)非同期メソッドの場合、暗黙的なローカル変数はタスク戻り変数とも呼ばれ、型がTで、初期値がTの既定値です。
  4. 非同期メソッドが戻り値の型TaskまたはTask(Of T)を持つ TFunctionである場合は、現在の呼び出しに関連付けられた、その型のオブジェクトが暗黙的に作成されます。 これは 非同期オブジェクト と呼ばれ、非同期メソッドのインスタンスを実行することによって実行される将来の作業を表します。 この非同期メソッド インスタンスの呼び出し元で制御が再開されると、呼び出し元は呼び出しの結果としてこの非同期オブジェクトを受け取ります。
  5. その後、インスタンスの制御ポイントが非同期メソッド本体の最初のステートメントで設定され、そこからメソッド本体の実行が直ちに開始されます (セクション ブロックとラベル)。

再開デリゲートと現在の呼び出し元

セクション Await 演算子で詳しく説明したように、 Await 式の実行には、制御フローを残して他の場所に移動するメソッド インスタンスの制御ポイントを中断する機能があります。 制御フローは、再開デリゲートを呼び出して、同じインスタンスの制御ポイントで後で 再開できます。 この中断は非同期メソッドを終了せずに行われ、最終的にハンドラーが実行されることはありません。 メソッド インスタンスは、再開デリゲートと Task または Task(Of T) 結果 (存在する場合) の両方によって引き続き参照され、デリゲートまたは結果へのライブ参照が存在する限りガベージ コレクションされません。

このステートメントは、次の構文の短縮形とほぼ同じ Dim x = Await WorkAsync() を想像すると役立ちます。

Dim temp = WorkAsync().GetAwaiter()
If Not temp.IsCompleted Then
       temp.OnCompleted(resumptionDelegate)
       Return [task]
       CONT:   ' invocation of 'resumptionDelegate' will resume here
End If
Dim x = temp.GetResult()

次の例では、メソッド インスタンスの 現在の呼び出し元 は、元の呼び出し元または再開デリゲートの呼び出し元のいずれか新しい呼び出し元として定義されています。

制御フローが非同期メソッド本体を終了する場合(終了を示す End Sub または End Function に到達するか、明示的な Return または Exit ステートメントを使用するか、ハンドルされない例外を通じて)、インスタンスの制御ポイントがメソッドの末尾に設定されます。 その後の動作は、非同期メソッドの戻り値の型によって異なります。

  • 戻り値の型がTaskAsync Functionの場合:

    1. 制御フローがハンドルされない例外を通じて終了した場合、非同期オブジェクトの状態は TaskStatus.Faulted に設定され、そのException.InnerExceptionプロパティは例外に設定されます (ただし、OperationCanceledExceptionTaskStatus.Canceledに変更するなど、特定の実装定義の例外を除く)。 現在の呼び出し元で制御フローが再開されます。

    2. それ以外の場合、非同期オブジェクトの状態は TaskStatus.Completedに設定されます。 現在の呼び出し元で制御フローが再開されます。

      (注。 Taskの要点と非同期メソッドが興味深いのは、タスクが完了すると、それを待っていたすべてのメソッドが現在再開デリゲートを実行するということです。つまり、ブロックが解除されます)。

  • 一部のTの戻り値の型がTask(Of T)Async Functionの場合、動作は上記のようになります。ただし、非例外の場合、非同期オブジェクトのResultプロパティもタスクの戻り値変数の最終的な値に設定されます。

  • Async Subの場合:

    1. 制御フローがハンドルされない例外を通じて終了した場合、その例外は実装固有の方法で環境に伝達されます。 現在の呼び出し元で制御フローが再開されます。
    2. それ以外の場合は、現在の呼び出し元で制御フローが再開されます。

Async Sub

Async Subの Microsoft 固有の動作がいくつかあります。

呼び出しの開始時に SynchronizationContext.CurrentNothing されると、Async Sub からの未処理の例外が Threadpool にポストされます。

SynchronizationContext.Currentが呼び出しの開始時にNothingされていない場合は、メソッドの開始前にそのコンテキストでOperationStarted()が呼び出され、終了後にOperationCompleted()されます。 さらに、ハンドルされない例外は、同期コンテキストで再スローされるようにポストされます。

つまり、UI アプリケーションでは、UI スレッドで呼び出される Async Sub の場合、処理に失敗した例外はすべて UI スレッドに再ポストされます。

非同期メソッドと反復子メソッドの変更可能な構造体

一般に変更可能な構造体は不適切な方法と見なされ、非同期メソッドまたは反復子メソッドではサポートされていません。 特に、構造体内の非同期メソッドまたは反復子メソッドの各呼び出しは、呼び出しの時点でコピーされるその構造体の コピー に対して暗黙的に動作します。 したがって、たとえば、次のようになります。

Structure S
       Dim x As Integer
       Async Sub Mutate()
           x = 2
       End Sub
End Structure

Dim s As New S With {.x = 1}
s.Mutate()
Console.WriteLine(s.x)   ' prints "1"

ブロックとラベル

実行可能ステートメントのグループは、ステートメント ブロックと呼ばれます。 ステートメント ブロックの実行は、ブロック内の最初のステートメントで始まります。 ステートメントが実行されると、ステートメントが他の場所に実行を転送しないか、例外が発生しない限り、構文上の順序で次のステートメントが実行されます。

ステートメント ブロック内では、ラベル宣言ステートメントを除き、論理行でのステートメントの除算は重要ではありません。 ラベルは、 GoToなどの分岐ステートメントのターゲットとして使用できるステートメント ブロック内の特定の位置を識別する識別子です。

Block
    : Statements*
    ;

LabelDeclarationStatement
    : LabelName ':'
    ;

LabelName
    : Identifier
    | IntLiteral
    ;

Statements
    : Statement? ( ':' Statement? )*
    ;

ラベル宣言ステートメントは論理行の先頭に記述する必要があり、ラベルには識別子または整数リテラルを指定できます。 ラベル宣言ステートメントと呼び出しステートメントはどちらも 1 つの識別子で構成できるため、ローカル行の先頭にある 1 つの識別子は常にラベル宣言ステートメントと見なされます。 同じ論理行に続くステートメントがない場合でも、ラベル宣言ステートメントの後には必ずコロンを付ける必要があります。

ラベルには独自の宣言領域があり、他の識別子に干渉することはありません。 次の例は有効であり、パラメーターとラベルの両方 x 名前変数を使用します。

Function F(x As Integer) As Integer
    If x >= 0 Then
        GoTo x
    End If
    x = -x
x: 
    Return x
End Function

ラベルのスコープは、ラベルを含むメソッドの本体です。

読みやすくするために、複数のサブステートメントを含むステートメントの実稼働は、この仕様では 1 つの実稼働として扱われます。ただし、サブステートメントはそれぞれラベル付き行にある場合があります。

ローカル変数とパラメーター

前のセクションでは、メソッド インスタンスを作成する方法とタイミング、およびメソッドのローカル変数とパラメーターのコピーについて詳しく説明します。 さらに、ループの本体が入力されるたびに、セクション ループ ステートメントで説明されているように、そのループ内で宣言された各ローカル変数から新しいコピーが作成され、メソッド インスタンスには、前のコピーではなくローカル変数のこのコピーが含まれるようになりました。

すべてのローカルは、型の既定値に初期化されます。 ローカル変数とパラメーターは常にパブリックにアクセスできます。 次の例に示すように、宣言の前にあるテキスト位置のローカル変数を参照するのはエラーです。

Class A
    Private i As Integer = 0

    Sub F()
        i = 1
        Dim i As Integer       ' Error, use precedes declaration.
        i = 2
    End Sub

    Sub G()
        Dim a As Integer = 1
        Dim b As Integer = a   ' This is valid.
    End Sub
End Class

上記の F メソッドでは、 i への最初の割り当ては、外側のスコープで宣言されたフィールドを特に参照していません。 代わりに、ローカル変数を参照し、変数の宣言の前にテキストで記述されるため、エラーになります。 G メソッドでは、後続の変数宣言は、同じローカル変数宣言内の以前の変数宣言で宣言されたローカル変数を参照します。

メソッド内の各ブロックは、ローカル変数の宣言領域を作成します。 この宣言空間には、メソッド本体のローカル変数宣言と、メソッドのパラメーター リストを使用して名前が導入されます。これにより、最も外側のブロックの宣言空間に名前が導入されます。 ブロックでは、入れ子を使用した名前のシャドウは許可されません。ブロック内で名前が宣言されると、入れ子になったブロックで名前が再宣言されない可能性があります。

したがって、次の例では、 F メソッドと G メソッドがエラーになります。これは、名前 i が外側のブロックで宣言されており、内部ブロックで再宣言できないためです。 ただし、 H メソッドと I メソッドは有効です。2 つの iは、入れ子になっていない個別のブロックで宣言されているためです。

Class A
    Sub F()
        Dim i As Integer = 0
        If True Then
               Dim i As Integer = 1
        End If
    End Sub

    Sub G()
        If True Then
            Dim i As Integer = 0
        End If
        Dim i As Integer = 1
    End Sub 

    Sub H()
        If True Then
            Dim i As Integer = 0
        End If
        If True Then
            Dim i As Integer = 1
        End If
    End Sub

    Sub I() 
        For i As Integer = 0 To 9
            H()
        Next i

        For i As Integer = 0 To 9
            H()
        Next i
    End Sub 
End Class

メソッドが関数の場合、特殊なローカル変数は、関数の戻り値を表すメソッドと同じ名前で、メソッド本体の宣言空間で暗黙的に宣言されます。 ローカル変数は、式で使用する場合に特別な名前解決セマンティクスを持ちます。 呼び出し式など、メソッド グループとして分類された式を必要とするコンテキストでローカル変数が使用されている場合、名前はローカル変数ではなく関数に解決されます。 例えば次が挙げられます。

Function F(i As Integer) As Integer
    If i = 0 Then
        F = 1        ' Sets the return value.
    Else
        F = F(i - 1) ' Recursive call.
    End If
End Function

かっこを使用すると、あいまいな状況 ( F(1)、戻り値の型が 1 次元配列である関数 F など) が発生する可能性があります。あいまいな状況では、名前はローカル変数ではなく関数に解決されます。 例えば次が挙げられます。

Function F(i As Integer) As Integer()
    If i = 0 Then
        F = new Integer(2) { 1, 2, 3 }
    Else
        F = F(i - 1) ' Recursive call, not an index.
    End If
End Function

制御フローがメソッド本体から離れると、ローカル変数の値が呼び出し式に戻されます。 メソッドがサブルーチンの場合、このような暗黙的なローカル変数はなく、制御は単に呼び出し式に戻ります。

ローカル宣言ステートメント

ローカル宣言ステートメントは、新しいローカル変数、ローカル定数、または静的変数を宣言します。 ローカル変数ローカル定数 は、メソッドをスコープにしたインスタンス変数と定数に相当し、同じ方法で宣言されます。 静的変数Shared 変数に似ていますが、 Static 修飾子を使用して宣言されます。

LocalDeclarationStatement
    : LocalModifier VariableDeclarators StatementTerminator
    ;

LocalModifier
    : 'Static' | 'Dim' | 'Const'
    ;

静的変数は、メソッドの呼び出し間で値を保持するローカルです。 非共有メソッド内で宣言される静的変数はインスタンスごとです。メソッドを含む型の各インスタンスには、静的変数の独自のコピーがあります。 Sharedメソッド内で宣言された静的変数は型ごとに、すべてのインスタンスに静的変数のコピーは 1 つだけあります。 ローカル変数はメソッドへの各エントリで型の既定値に初期化されますが、静的変数は型または型インスタンスが初期化されるときにだけ型の既定値に初期化されます。 静的変数は、構造体またはジェネリック メソッドでは宣言できません。

ローカル変数、ローカル定数、および静的変数は常にパブリック アクセシビリティを持ち、アクセシビリティ修飾子を指定しない場合があります。 ローカル宣言ステートメントで型が指定されていない場合、次の手順でローカル宣言の型が決まります。

  1. 宣言に型文字がある場合、型文字の型はローカル宣言の型です。

  2. ローカル宣言がローカル定数の場合、またはローカル宣言が初期化子を持つローカル変数で、ローカル変数の型推論が使用されている場合、ローカル宣言の型は初期化子の型から推論されます。 初期化子がローカル宣言を参照している場合は、コンパイル時エラーが発生します。 (初期化子を持つにはローカル定数が必要です)。

  3. 厳密なセマンティクスが使用されていない場合、ローカル宣言ステートメントの型は暗黙的に Object

  4. それ以外の場合は、コンパイル時エラーが発生します。

配列サイズまたは配列型修飾子を持つローカル宣言ステートメントで型が指定されていない場合、ローカル宣言の型は、指定されたランクを持つ配列であり、前の手順を使用して配列の要素型を決定します。 ローカル変数の型推論を使用する場合、初期化子の型は、ローカル宣言ステートメントと同じ配列図形 (つまり、配列型修飾子) を持つ配列型である必要があります。 推論された要素型が配列型である可能性があることに注意してください。 例えば次が挙げられます。

Option Infer On

Module Test
    Sub Main()
        ' Error: initializer is not an array type
        Dim x() = 1

        ' Type is Integer()
        Dim y() = New Integer() {}

        ' Type is Integer()()
        Dim z() = New Integer()() {}

        ' Type is Integer()()()

        Dim a()() = New Integer()()() {}

        ' Error: initializer does not have same array shape
        Dim b()() = New Integer(,)() {}
    End Sub
End Module

null 許容型修飾子を持つローカル宣言ステートメントで型が指定されていない場合、ローカル宣言の型は、推論された型の null 許容バージョン、または null 許容値型が既にある場合は推論された型自体になります。 推論された型が null 許容にできる値型でない場合は、コンパイル時エラーが発生します。 null 許容型修飾子と配列サイズまたは配列型修飾子の両方が型のないローカル宣言ステートメントに配置されている場合、null 許容型修飾子は配列の要素型に適用されると見なされ、前の手順を使用して要素の型が決定されます。

ローカル宣言ステートメントの変数初期化子は、宣言のテキスト位置に配置された代入ステートメントと同等です。 したがって、実行がローカル宣言ステートメントに分岐する場合、変数初期化子は実行されません。 ローカル宣言ステートメントが複数回実行された場合、変数初期化子は同じ回数だけ実行されます。 静的変数は、初期化子を初めて実行するだけです。 静的変数の初期化中に例外が発生した場合、静的変数は静的変数の型の既定値で初期化されたと見なされます。

初期化子の使用例を次に示します。

Module Test
    Sub F()
        Static x As Integer = 5

        Console.WriteLine("Static variable x = " & x)
        x += 1
    End Sub

    Sub Main()
        Dim i As Integer

        For i = 1 to 3
            F()
        Next i

        i = 3
label:
        Dim y As Integer = 8

        If i > 0 Then
            Console.WriteLine("Local variable y = " & y)
            y -= 1
            i -= 1
            GoTo label
        End If
    End Sub
End Module

このプログラムは次の内容を出力します。

Static variable x = 5
Static variable x = 6
Static variable x = 7
Local variable y = 8
Local variable y = 8
Local variable y = 8

静的ローカルの初期化子はスレッド セーフであり、初期化中に例外から保護されます。 静的ローカル初期化子中に例外が発生した場合、静的ローカルには既定値が設定され、初期化されません。 静的ローカル初期化子

Module Test
    Sub F()
        Static x As Integer = 5
    End Sub
End Module

は、次の値に相当します。

Imports System.Threading
Imports Microsoft.VisualBasic.CompilerServices

Module Test
    Class InitFlag
        Public State As Short
    End Class

    Private xInitFlag As InitFlag = New InitFlag()

    Sub F()
        Dim x As Integer

        If xInitFlag.State <> 1 Then
            Monitor.Enter(xInitFlag)
            Try
                If xInitFlag.State = 0 Then
                    xInitFlag.State = 2
                    x = 5
                Else If xInitFlag.State = 2 Then
                    Throw New IncompleteInitialization()
                End If
            Finally
                xInitFlag.State = 1
                Monitor.Exit(xInitFlag)
            End Try
        End If
    End Sub
End Module

ローカル変数、ローカル定数、および静的変数のスコープは、宣言されているステートメント ブロックに設定されます。 静的変数は、メソッド全体で名前を 1 回しか使用できないという点で特別です。 たとえば、異なるブロック内にある場合でも、同じ名前の 2 つの静的変数宣言を指定することは有効ではありません。

暗黙的なローカル宣言

ローカル宣言ステートメントに加えて、ローカル変数を使用して暗黙的に宣言することもできます。 他の何かに解決されない名前を使用する単純な名前式は、その名前でローカル変数を宣言します。 例えば次が挙げられます。

Option Explicit Off

Module Test
    Sub Main()
        x = 10
        y = 20
        Console.WriteLine(x + y)
    End Sub
End Module

暗黙的なローカル宣言は、変数として分類された式を受け入れることができる式コンテキストでのみ発生します。 この規則の例外は、ローカル変数が関数呼び出し式、インデックス作成式、またはメンバー アクセス式のターゲットである場合に暗黙的に宣言されない可能性がある点です。

暗黙的なローカルは、包含メソッドの先頭で宣言されているかのように扱われます。 したがって、ラムダ式内で宣言されている場合でも、常にメソッド本体全体にスコープが設定されます。 たとえば、次のコードです。

Option Explicit Off 

Module Test
    Sub Main()
        Dim x = Sub()
                    a = 10
                End Sub
        Dim y = Sub()
                    Console.WriteLine(a)
                End Sub

        x()
        y()
    End Sub
End Module

は値を 10出力します。 暗黙的なローカルは、変数名に型文字がアタッチされていない場合は Object として型指定されます。それ以外の場合、変数の型は型文字の型です。 ローカル変数の型推論は、暗黙的なローカルには使用されません。

コンパイル環境または Option Explicitによって明示的なローカル宣言が指定されている場合、すべてのローカル変数を明示的に宣言する必要があり、暗黙的な変数宣言は許可されません。

With ステートメント

With ステートメントでは、式を複数回指定せずに、式のメンバーを複数参照できます。

WithStatement
    : 'With' Expression StatementTerminator
      Block?
      'End' 'With' StatementTerminator
    ;

式は値として分類する必要があり、ブロックに入ると 1 回評価されます。 With ステートメント ブロック内では、ピリオドまたは感嘆符で始まるメンバー アクセス式またはディクショナリ アクセス式は、With式の前にあるかのように評価されます。 例えば次が挙げられます。

Structure Test
    Public x As Integer

    Function F() As Integer
        Return 10
    End Function
End Structure

Module TestModule
    Sub Main()
        Dim y As Test

        With y
            .x = 10
            Console.WriteLine(.x)
            .x = .F()
        End With
    End Sub
End Module

ブロックの外側から With ステートメント ブロックに分岐することは無効です。

SyncLock ステートメント

SyncLock ステートメントを使用すると、式でステートメントを同期できます。これにより、複数の実行スレッドが同じステートメントを同時に実行しないようにすることができます。

SyncLockStatement
    : 'SyncLock' Expression StatementTerminator
      Block?
      'End' 'SyncLock' StatementTerminator
    ;

式は値として分類する必要があり、ブロックへの入力時に 1 回評価されます。 SyncLock ブロックに入ると、指定した式に対してShared メソッド System.Threading.Monitor.Enterが呼び出されます。このメソッドは、実行スレッドが式によって返されるオブジェクトに対して排他ロックを持つまでブロックします。 SyncLock ステートメント内の式の型は、参照型である必要があります。 例えば次が挙げられます。

Class Test
    Private count As Integer = 0

    Public Function Add() As Integer
        SyncLock Me
            count += 1
            Add = count
        End SyncLock
    End Function

    Public Function Subtract() As Integer
        SyncLock Me
            count -= 1
            Subtract = count
        End SyncLock
    End Function
End Class

上記の例では、クラス Test の特定のインスタンスで同期を行い、特定のインスタンスに対して count 変数を一度に 1 つ以上の実行スレッドが追加または減算できないようにします。

SyncLock ブロックは、Finally ブロックが式にSystem.Threading.Monitor.ExitShared メソッドを呼び出すTry ステートメントによって暗黙的に含まれます。 これにより、例外がスローされた場合でもロックが解放されます。 その結果、ブロックの外部から SyncLock ブロックに分岐することは無効であり、 SyncLock ブロックは、 ResumeResume Nextの目的で単一のステートメントとして扱われます。 上記の例は、次のコードと同じです。

Class Test
    Private count As Integer = 0

    Public Function Add() As Integer
        Try
            System.Threading.Monitor.Enter(Me)

            count += 1
            Add = count
        Finally
            System.Threading.Monitor.Exit(Me)
        End Try
    End Function

    Public Function Subtract() As Integer
        Try
            System.Threading.Monitor.Enter(Me)

            count -= 1
            Subtract = count
        Finally
            System.Threading.Monitor.Exit(Me)
        End Try
    End Function
End Class

Event ステートメント

RaiseEventAddHandler、およびRemoveHandlerステートメントは、イベントを発生させ、イベントを動的に処理します。

EventStatement
    : RaiseEventStatement
    | AddHandlerStatement
    | RemoveHandlerStatement
    ;

RaiseEvent ステートメント

RaiseEvent ステートメントは、特定のイベントが発生したことをイベント ハンドラーに通知します。

RaiseEventStatement
    : 'RaiseEvent' IdentifierOrKeyword
      ( OpenParenthesis ArgumentList? CloseParenthesis )? StatementTerminator
    ;

RaiseEvent ステートメントの単純な名前式は、Meのメンバー参照として解釈されます。 したがって、 RaiseEvent xRaiseEvent Me.xされたかのように解釈されます。 式の結果は、クラス自体で定義されているイベントのイベント アクセスとして分類する必要があります。基本型で定義されたイベントは、 RaiseEvent ステートメントでは使用できません。

RaiseEvent ステートメントは、指定されたパラメーター (存在する場合) を使用して、イベントのデリゲートのInvoke メソッドの呼び出しとして処理されます。 デリゲートの値が Nothing場合、例外はスローされません。 引数がない場合は、かっこを省略できます。 例えば次が挙げられます。

Class Raiser
    Public Event E1(Count As Integer)

    Public Sub Raise()
        Static RaiseCount As Integer = 0

        RaiseCount += 1
        RaiseEvent E1(RaiseCount)
    End Sub
End Class

Module Test
    Private WithEvents x As Raiser

    Private Sub E1Handler(Count As Integer) Handles x.E1
        Console.WriteLine("Raise #" & Count)
    End Sub

    Public Sub Main()
        x = New Raiser
        x.Raise()        ' Prints "Raise #1".
        x.Raise()        ' Prints "Raise #2".
        x.Raise()        ' Prints "Raise #3".
    End Sub
End Module

上記 Raiser クラスは次のようになります。

Class Raiser
    Public Event E1(Count As Integer)

    Public Sub Raise()
        Static RaiseCount As Integer = 0
        Dim TemporaryDelegate As E1EventHandler

        RaiseCount += 1

        ' Use a temporary to avoid a race condition.
        TemporaryDelegate = E1Event
        If Not TemporaryDelegate Is Nothing Then
            TemporaryDelegate.Invoke(RaiseCount)
        End If
    End Sub
End Class

AddHandler ステートメントと RemoveHandler ステートメント

ほとんどのイベント ハンドラーは、 WithEvents 変数を介して自動的にフックされますが、実行時にイベント ハンドラーを動的に追加および削除することが必要な場合があります。 AddHandler ステートメントと RemoveHandler ステートメントでこれを行います。

AddHandlerStatement
    : 'AddHandler' Expression Comma Expression StatementTerminator
    ;

RemoveHandlerStatement
    : 'RemoveHandler' Expression Comma Expression StatementTerminator
    ;

各ステートメントは 2 つの引数を受け取ります。最初の引数はイベント アクセスとして分類される式である必要があり、2 番目の引数は値として分類される式である必要があります。 2 番目の引数の型は、イベント アクセスに関連付けられているデリゲート型である必要があります。 例えば次が挙げられます。

Public Class Form1
    Public Sub New()
        ' Add Button1_Click as an event handler for Button1's Click event.
        AddHandler Button1.Click, AddressOf Button1_Click
    End Sub 

    Private Button1 As Button = New Button()

    Sub Button1_Click(sender As Object, e As EventArgs)
        Console.WriteLine("Button1 was clicked!")
    End Sub

    Public Sub Disconnect()
        RemoveHandler Button1.Click, AddressOf Button1_Click
    End Sub
End Class

イベント E, 場合、ステートメントはインスタンスの関連する add_E または remove_E メソッドを呼び出して、デリゲートをイベントのハンドラーとして追加または削除します。 したがって、上記のコードは次のようになります。

Public Class Form1
    Public Sub New()
        Button1.add_Click(AddressOf Button1_Click)
    End Sub 

    Private Button1 As Button = New Button()

    Sub Button1_Click(sender As Object, e As EventArgs)
        Console.WriteLine("Button1 was clicked!")
    End Sub

    Public Sub Disconnect()
        Button1.remove_Click(AddressOf Button1_Click)
    End Sub
End Class

Assignment ステートメント

代入ステートメントは、式の値を変数に割り当てます。 割り当てにはいくつかの種類があります。

AssignmentStatement
    : RegularAssignmentStatement
    | CompoundAssignmentStatement
    | MidAssignmentStatement
    ;

通常の代入ステートメント

単純な代入ステートメントは、式の結果を変数に格納します。

RegularAssignmentStatement
    : Expression Equals Expression StatementTerminator
    ;

代入演算子の左側の式は変数またはプロパティ アクセスとして分類する必要があります。一方、代入演算子の右側の式は値として分類する必要があります。 式の型は、変数またはプロパティ アクセスの型に暗黙的に変換できる必要があります。

割り当てられている変数が参照型の配列要素である場合は、式が配列要素型と互換性があることを確認する実行時チェックが実行されます。 次の例では、ArrayListのインスタンスをString配列の要素に格納できないため、最後の割り当てによってSystem.ArrayTypeMismatchExceptionがスローされます。

Dim sa(10) As String
Dim oa As Object() = sa
oa(0) = Nothing         ' This is allowed.
oa(1) = "Hello"         ' This is allowed.
oa(2) = New ArrayList() ' System.ArrayTypeMismatchException is thrown.

代入演算子の左側の式が変数として分類されている場合、代入ステートメントは変数に値を格納します。 式がプロパティ アクセスとして分類されている場合、assignment ステートメントはプロパティ アクセスを、値パラメーターに置き換えたプロパティの Set アクセサーの呼び出しに変換します。 例えば次が挙げられます。

Module Test
    Private PValue As Integer

    Public Property P As Integer
        Get
            Return PValue
        End Get

        Set (Value As Integer)
            PValue = Value
        End Set
    End Property

    Sub Main()
        ' The following two lines are equivalent.
        P = 10
        set_P(10)
    End Sub
End Module

変数またはプロパティ アクセスのターゲットが値型として型指定されているが、変数として分類されていない場合は、コンパイル時エラーが発生します。 例えば次が挙げられます。

Structure S
    Public F As Integer
End Structure

Class C
    Private PValue As S

    Public Property P As S
        Get
            Return PValue
        End Get

        Set (Value As S)
            PValue = Value
        End Set
    End Property
End Class

Module Test
    Sub Main()
        Dim ct As C = New C()
        Dim rt As Object = new C()

        ' Compile-time error: ct.P not classified as variable.
        ct.P.F = 10

        ' Run-time exception.
        rt.P.F = 10
    End Sub
End Module

代入のセマンティクスは、割り当てられている変数またはプロパティの型によって異なります。 代入先の変数が値型の場合、代入によって式の値が変数にコピーされます。 代入先の変数が参照型の場合、代入は値自体ではなく参照を変数にコピーします。 変数の型が Object場合、代入セマンティクスは、値の型が値型であるか、実行時に参照型であるかによって決まります。

"注: IntegerDateなどの組み込み型の場合、型は不変であるため、参照と値の割り当てのセマンティクスは同じです。 その結果、言語は、ボックス化された組み込み型に対する参照割り当てを最適化として自由に使用できます。 値の観点からは、結果は同じです。

等号文字 (=) は代入と等値の両方に使用されるため、単純な代入と呼び出しステートメントの間にはあいまいさが x = y.ToString()などの状況で存在します。 このような場合はすべて、代入ステートメントが等値演算子よりも優先されます。 つまり、式の例は、(x = y).ToString()ではなくx = (y.ToString())として解釈されます。

複合代入ステートメント

複合代入ステートメントV op= E形式になります (ここで、opは有効な二項演算子です)。

CompoundAssignmentStatement
    : Expression CompoundBinaryOperator LineTerminator? Expression StatementTerminator
    ;

CompoundBinaryOperator
    : '^' '=' | '*' '=' | '/' '=' | '\\' '=' | '+' '=' | '-' '='
    | '&' '=' | '<' '<' '=' | '>' '>' '='
    ;

代入演算子の左側の式は変数またはプロパティ アクセスとして分類する必要があります。代入演算子の右側の式は値として分類する必要があります。 複合代入ステートメントは、複合代入演算子の左側の変数が 1 回だけ評価されるという違いを持つステートメント V = V op E と同じです。 この相違点を次の例で示します。

Module Test
    Function GetIndex() As Integer
        Console.WriteLine("Getting index")
        Return 1
    End Function

    Sub Main()
        Dim a(2) As Integer

        Console.WriteLine("Simple assignment")
        a(GetIndex()) = a(GetIndex()) + 1

        Console.WriteLine("Compound assignment")
        a(GetIndex()) += 1
    End Sub
End Module

a(GetIndex()) は、単純な代入では 2 回評価されますが、複合代入の場合は 1 回だけ評価されるため、コードは次を出力します。

Simple assignment
Getting index
Getting index
Compound assignment
Getting index

Mid Assignment ステートメント

Mid代入ステートメントは、文字列を別の文字列に割り当てます。 代入の左側には、関数 Microsoft.VisualBasic.Strings.Midの呼び出しと同じ構文があります。

MidAssignmentStatement
    : 'Mid' '$'? OpenParenthesis Expression Comma Expression
      ( Comma Expression )? CloseParenthesis Equals Expression StatementTerminator
    ;

最初の引数は代入のターゲットであり、 Stringとの間で暗黙的に変換可能な型を持つ変数またはプロパティ アクセスとして分類する必要があります。 2 番目のパラメーターは 1 から始まる開始位置です。これは、ターゲット文字列で割り当てを開始する位置に対応し、型を Integerに暗黙的に変換できる値として分類する必要があります。 省略可能な 3 番目のパラメーターは、ターゲット文字列に割り当てる右側の値の文字数であり、型が暗黙的に Integerに変換できる値として分類する必要があります。 右側はソース文字列であり、型が暗黙的に Stringに変換できる値として分類する必要があります。 指定した場合、右側は length パラメーターに切り捨てられ、開始位置から始まる左側の文字列内の文字が置き換えられます。 右側の文字列に含まれる文字数が 3 番目のパラメーターよりも少ない場合は、右側の文字列の文字のみがコピーされます。

次の例では、 ab123fgを表示します。

Module Test
    Sub Main()
        Dim s1 As String = "abcdefg"
        Dim s2 As String = "1234567"

        Mid$(s1, 3, 3) = s2
        Console.WriteLine(s1)
    End Sub
End Module

"注: Mid は予約語ではありません。

呼び出しステートメント

呼び出しステートメントは、省略可能なキーワード Callが先行するメソッドを呼び出します。 呼び出しステートメントは、関数呼び出し式と同じ方法で処理され、以下に示す相違点がいくつかあります。 呼び出し式は、値または void として分類する必要があります。 呼び出し式の評価に起因するすべての値は破棄されます。

Call キーワードを省略した場合、呼び出し式は識別子またはキーワードで始まるか、With ブロック内の.で始まる必要があります。 したがって、たとえば、"Call 1.ToString()" は有効なステートメントですが、"1.ToString()" は有効ではありません。 (式コンテキストでは、呼び出し式も識別子で始まる必要はありません。たとえば、"Dim x = 1.ToString()" は有効なステートメントです)。

呼び出しステートメントと呼び出し式にはもう 1 つの違いがあります。呼び出しステートメントに引数リストが含まれている場合、これは常に呼び出しの引数リストとして使用されます。 次の例は、その違いを示しています。

Module Test
    Sub Main()
        Call {Function() 15}(0)
        ' error: (0) is taken as argument list, but array is not invokable

        Call ({Function() 15}(0))
        ' valid, since the invocation statement has no argument list

        Dim x = {Function() 15}(0)
        ' valid as an expression, since (0) is taken as an array-indexing

        Call f("a")
        ' error: ("a") is taken as argument list to the invocation of f

        Call f()("a")
        ' valid, since () is the argument list for the invocation of f

        Dim y = f("a")
        ' valid as an expression, since f("a") is interpreted as f()("a")
    End Sub

    Sub f() As Func(Of String,String)
        Return Function(x) x
    End Sub
End Module
InvocationStatement
    : 'Call'? InvocationExpression StatementTerminator
    ;

条件文

条件ステートメントを使用すると、実行時に評価される式に基づいてステートメントを条件付きで実行できます。

ConditionalStatement
    : IfStatement
    | SelectStatement
    ;

もし。。。そうしたら。。。Else ステートメント

If...Then...Elseステートメントは、基本的な条件付きステートメントです。

IfStatement
    : BlockIfStatement
    | LineIfThenStatement
    ;

BlockIfStatement
    : 'If' BooleanExpression 'Then'? StatementTerminator
      Block?
      ElseIfStatement*
      ElseStatement?
      'End' 'If' StatementTerminator
    ;

ElseIfStatement
    : ElseIf BooleanExpression 'Then'? StatementTerminator
      Block?
    ;

ElseStatement
    : 'Else' StatementTerminator
      Block?
    ;

LineIfThenStatement
    : 'If' BooleanExpression 'Then' Statements ( 'Else' Statements )? StatementTerminator
    ;
    
ElseIf		
	: 'ElseIf'		
	| 'Else' 'If'   
   ;

セクションブール式に従って、 If...Then...Else ステートメント内の各式はブール である必要があります。 (注: 式にブール型を指定する必要はありません)。 If ステートメント内の式が true の場合は、If ブロックで囲まれたステートメントが実行されます。 式が false の場合、各 ElseIf 式が評価されます。 ElseIf式のいずれかが true と評価された場合、対応するブロックが実行されます。 式が true と評価され、 Else ブロックがある場合は、 Else ブロックが実行されます。 ブロックの実行が完了すると、実行は If...Then...Else ステートメントの末尾に渡されます。

If ステートメントの行バージョンには、If式がTrueされている場合に実行されるステートメントのセットが 1 つあり、式がFalseされている場合に実行されるオプションのステートメントのセットがあります。 例えば次が挙げられます。

Module Test
    Sub Main()
        Dim a As Integer = 10
        Dim b As Integer = 20

        ' Block If statement.
        If a < b Then
            a = b
        Else
            b = a
        End If

        ' Line If statement
        If a < b Then a = b Else b = a
    End Sub
End Module

If ステートメントの行バージョンは 、":" よりも厳密にバインドされません。その Else は構文で許可されている構文に最も近い前の If にバインドされます。 たとえば、次の 2 つのバージョンは同等です。

If True Then _
If True Then Console.WriteLine("a") Else Console.WriteLine("b") _
Else Console.WriteLine("c") : Console.WriteLine("d")

If True Then
    If True Then
        Console.WriteLine("a")
    Else
        Console.WriteLine("b")
    End If
    Console.WriteLine("c") : Console.WriteLine("d")
End If

label 宣言ステートメント以外のすべてのステートメントは、ブロック ステートメントを含む行 If ステートメント内で許可されます。 ただし、複数行のラムダ式内を除き、LineTerminators を StatementTerminators として使用することはできません。 例えば次が挙げられます。

' Allowed, since it uses : instead of LineTerminator to separate statements
If b Then With New String("a"(0),5) : Console.WriteLine(.Length) : End With

' Disallowed, since it uses a LineTerminator
If b then With New String("a"(0), 5)
              Console.WriteLine(.Length)
          End With

' Allowed, since it only uses LineTerminator inside a multi-line lambda
If b Then Call Sub()
                   Console.WriteLine("a")
               End Sub.Invoke()

Select Case ステートメント

Select Case ステートメントは、式の値に基づいてステートメントを実行します。

SelectStatement
    : 'Select' 'Case'? Expression StatementTerminator
      CaseStatement*
      CaseElseStatement?
      'End' 'Select' StatementTerminator
    ;

CaseStatement
    : 'Case' CaseClauses StatementTerminator
      Block?
    ;

CaseClauses
    : CaseClause ( Comma CaseClause )*
    ;

CaseClause
    : ( 'Is' LineTerminator? )? ComparisonOperator LineTerminator? Expression
    | Expression ( 'To' Expression )?
    ;

ComparisonOperator
    : '=' | '<' '>' | '<' | '>' | '>' '=' | '<' '='
    ;

CaseElseStatement
    : 'Case' 'Else' StatementTerminator
      Block?
    ;

式は値として分類する必要があります。 Select Case ステートメントを実行すると、最初にSelect式が評価され、Caseステートメントがテキスト宣言の順序で評価されます。 Trueに評価される最初のCase ステートメントでは、そのブロックが実行されます。 CaseステートメントがTrueに評価されておらず、Case Elseステートメントがある場合は、そのブロックが実行されます。 ブロックの実行が完了すると、実行は Select ステートメントの末尾に渡されます。

Case ブロックの実行は、次の switch セクションに "フォールスルー" することは許可されていません。 これにより、 Case 終了ステートメントが誤って省略された場合に、他の言語で発生するバグの一般的なクラスを回避できます。 この動作を次の例に示します。

Module Test
    Sub Main()
        Dim x As Integer = 10

        Select Case x
            Case 5
                Console.WriteLine("x = 5")
            Case 10
                Console.WriteLine("x = 10")
            Case 20 - 10
                Console.WriteLine("x = 20 - 10")
            Case 30
                Console.WriteLine("x = 30")
        End Select
    End Sub
End Module

次のコードが出力されます。

x = 10

同じ値 Case 10 選択 Case 20 - 10 が、 Case 10 はテキスト Case 20 - 10 先行するため実行されます。 次の Case に達すると、 Select ステートメントの後に実行が続行されます。

Case句には 2 つの形式を使用できます。 1 つの形式は、省略可能な Is キーワード、比較演算子、および式です。 式は Select 式の型に変換されます。式が Select 式の型に暗黙的に変換できない場合は、コンパイル時エラーが発生します。 Select式が E、比較演算子が OpCase式が E1 の場合、ケースは E OP E1 として評価されます。 演算子は、2 つの式の型に対して有効である必要があります。それ以外の場合は、コンパイル時エラーが発生します。

もう 1 つの形式は、必要に応じて式の後にキーワード To と 2 番目の式が続く形式です。 どちらの式も Select 式の型に変換されます。どちらの式も Select 式の型に暗黙的に変換できない場合は、コンパイル時エラーが発生します。 Select式がE、最初のCase式がE1、2 番目のCase式がE2場合、CaseE = E1 (E2が指定されていない場合) または(E >= E1) And (E <= E2)として評価されます。 演算子は、2 つの式の型に対して有効である必要があります。それ以外の場合は、コンパイル時エラーが発生します。

ループ ステートメント

ループ ステートメントを使用すると、その本体でステートメントを繰り返し実行できます。

LoopStatement
    : WhileStatement
    | DoLoopStatement
    | ForStatement
    | ForEachStatement
    ;

ループ本体が入力されるたびに、その本体で宣言されているすべてのローカル変数から新しいコピーが作成され、変数の前の値に初期化されます。 ループ本体内の変数への参照では、最後に作成されたコピーが使用されます。 このコードは、次の例を示しています。

Module Test
    Sub Main()
        Dim lambdas As New List(Of Action)
        Dim x = 1

        For i = 1 To 3
            x = i
            Dim y = x
            lambdas.Add(Sub() Console.WriteLine(x & y))
        Next

        For Each lambda In lambdas
            lambda()
        Next
    End Sub
End Module

このコードでは、次の出力が生成されます。

31    32    33

ループ本体が実行されると、変数のコピーが現在のコピーを使用します。 たとえば、 Dim y = x ステートメントは、 y の最新のコピーと xの元のコピーを参照します。 また、ラムダが作成されると、変数の作成時に現在の変数のコピーが記憶されます。 したがって、各ラムダは、 xの同じ共有コピーを使用しますが、 yの異なるコピーを使用します。 プログラムの最後に、ラムダを実行すると、それらがすべて参照する x の共有コピーが最終的な値 3 になります。

ラムダや LINQ 式がない場合、ループ エントリで新しいコピーが作成されることを知ることは不可能であることに注意してください。 実際、コンパイラの最適化では、この場合はコピーの作成が回避されます。 ラムダまたは LINQ 式を含むループに GoTo することは無効であることにも注意してください。

つつ。。。End While と Do...ループ ステートメント

ブール式に基づいて、 While または Do ループ ステートメントがループします。

WhileStatement
    : 'While' BooleanExpression StatementTerminator
      Block?
      'End' 'While' StatementTerminator
    ;

DoLoopStatement
    : DoTopLoopStatement
    | DoBottomLoopStatement
    ;

DoTopLoopStatement
    : 'Do' ( WhileOrUntil BooleanExpression )? StatementTerminator
      Block?
      'Loop' StatementTerminator
    ;

DoBottomLoopStatement
    : 'Do' StatementTerminator
      Block?
      'Loop' WhileOrUntil BooleanExpression StatementTerminator
    ;

WhileOrUntil
    : 'While' | 'Until'
    ;

ブール式が true に評価される限り、 While ループ ステートメントはループします。 Do ループ ステートメントには、より複雑な条件が含まれている可能性があります。 式は、 Do キーワードの後、または Loop キーワードの後に配置できますが、両方の後に配置することはできません。 ブール式は、セクション ブール式に従って評価されます。 (注: 式にブール型を指定する必要はありません)。 式をまったく指定しないのも有効です。その場合、ループは終了しません。 式が Do後に配置された場合は、各イテレーションでループ ブロックが実行される前に評価されます。 式が Loop後に配置された場合は、各イテレーションでループ ブロックが実行された後に評価されます。 したがって、式を Loop の後に配置すると、 Do後の配置よりもループが 1 つ多くなります。 次の例でその動作を示します。

Module Test
    Sub Main()
        Dim x As Integer

        x = 3
        Do While x = 1
            Console.WriteLine("First loop")
        Loop

        Do
            Console.WriteLine("Second loop")
        Loop While x = 1
    End Sub
End Module

このコードでは、次の出力が生成されます。

Second Loop

最初のループの場合、ループが実行される前に条件が評価されます。 2 番目のループの場合、条件はループの実行後に実行されます。 条件式には、 While キーワードまたは Until キーワードのいずれかをプレフィックスとして付ける必要があります。 前者は、条件が false に評価された場合はループを中断し、後者は条件が true に評価された場合にループを中断します。

"注: Until は予約語ではありません。

対して。。。次のステートメント

For...Next ステートメントは、境界のセットに基づいてループします。 For ステートメントは、ループ 制御変数、下限式、上限式、およびオプションのステップ値式を指定します。 ループ制御変数は、識別子の後に省略可能な As 句または式を使用して指定します。

ForStatement
    : 'For' LoopControlVariable Equals Expression 'To' Expression
      ( 'Step' Expression )? StatementTerminator
      Block?
      ( 'Next' NextExpressionList? StatementTerminator )?
    ;

LoopControlVariable
    : Identifier ( IdentifierModifiers 'As' TypeName )?
    | Expression
    ;

NextExpressionList
    : Expression ( Comma Expression )*
    ;

次の規則に従って、ループ制御変数は、この For...Next ステートメントに固有の新しいローカル変数、または既存の変数、または式を参照します。

  • ループ制御変数が As 句を持つ識別子である場合、識別子は、 As 句で指定された型の新しいローカル変数を定義し、 For ループ全体をスコープとします。

  • ループ制御変数が As 句のない識別子である場合、識別子は最初に単純な名前解決規則 (セクション 単純名式を参照) を使用して解決されます。ただし、この識別子が出現しても、それ自体では暗黙的なローカル変数が作成されません (セクション 暗黙的なローカル宣言)。

    • この解決が成功し、結果が変数として分類される場合、ループ制御変数はその既存の変数になります。

    • 解決に失敗した場合、または解決が成功し、結果が型として分類された場合は、次のようになります。

      • ローカル変数の型推論が使用されている場合、識別子はバインドされた式とステップ式から型が推論される新しいローカル変数を定義し、 For ループ全体をスコープとします。
      • ローカル変数の型推論が使用されておらず、暗黙的なローカル宣言がある場合は、スコープがメソッド全体 (セクション 暗黙的なローカル宣言) である暗黙的なローカル変数が作成され、ループ制御変数はこの既存の変数を参照します。
      • ローカル変数の型推論も暗黙的なローカル宣言も使用されていない場合は、エラーになります。
    • 型と変数のどちらにも分類されていないもので解決が成功した場合は、エラーになります。

  • ループ 制御変数が式の場合は、式を変数として分類する必要があります。

ループ 制御変数は、別の囲み For...Next ステートメントでは使用できません。 For ステートメントのループ 制御変数の型は、イテレーションの型を決定し、次のいずれかである必要があります。

  • ByteSByteUShortShortUIntegerIntegerULongLongDecimalSingleDouble
  • 列挙型
  • Object
  • 次の演算子を持つ型 T 。ここで、 B はブール式で使用できる型です。
Public Shared Operator >= (op1 As T, op2 As T) As B
Public Shared Operator <= (op1 As T, op2 As T) As B
Public Shared Operator - (op1 As T, op2 As T) As T
Public Shared Operator + (op1 As T, op2 As T) As T

バインドされた式とステップ式は、ループ 制御変数の型に暗黙的に変換でき、値として分類する必要があります。 コンパイル時に、ループ コントロール変数の型は、下限、上限、およびステップ式の型の中で最も広い型を選択することによって推論されます。 2 つの型の間に拡大変換がない場合は、コンパイル時エラーが発生します。

実行時に、ループ コントロール変数の型が Objectされている場合、反復処理の型はコンパイル時と同じように推論され、2 つの例外があります。 まず、バインドされた式とステップ式がすべて整数型であり、最も広い型がない場合は、3 つの型すべてを含む最も広い型が推論されます。 2 つ目は、ループ 制御変数の型が Stringであると推論された場合、代わりに Double が推論されます。 実行時にループ コントロール型を特定できない場合、またはいずれかの式をループ コントロール型に変換できない場合は、 System.InvalidCastException が発生します。 ループの先頭でループ コントロール型が選択されると、ループ コントロール変数の値に加えられた変更に関係なく、イテレーション全体で同じ型が使用されます。

Forステートメントは、一致する Next ステートメントによって閉じる必要があります。 変数のない Next ステートメントは最も内側の開いている For ステートメントと一致しますが、1 つ以上のループ制御変数を持つ Next ステートメントは、左から右に、各変数に一致する For ループと一致します。 変数が、その時点で最も入れ子になったループではない For ループと一致すると、コンパイル時エラーが発生します。

ループの開始時に、3 つの式がテキスト順に評価され、下限の式がループ コントロール変数に割り当てられます。 ステップ値を省略すると、暗黙的にリテラル 1がループ 制御変数の型に変換されます。 3 つの式は、ループの開始時にのみ評価されます。

各ループの先頭で、制御変数が比較され、ステップ式が正の場合は終了点より大きいか、ステップ式が負の場合は終了点より小さくなります。 その場合、 For ループは終了し、それ以外の場合はループ ブロックが実行されます。 ループ 制御変数がプリミティブ型でない場合、比較演算子は、式 step >= step - step が true か false かによって決定されます。 Next ステートメントで、ステップ値が制御変数に追加され、実行がループの先頭に戻ります。

ループ コントロール変数の新しいコピーは、ループ ブロックの反復ごとに作成 されないこと に注意してください。 この点で、 For ステートメントは For Each (Section For Each...次のステートメント)。

ループの外側から For ループに分岐することは無効です。

For Each...次のステートメント

For Each...Nextステートメントは、式の要素に基づいてループします。 For Each ステートメントは、ループ 制御変数と列挙子式を指定します。 ループ制御変数は、識別子の後に省略可能な As 句または式を使用して指定します。

ForEachStatement
    : 'For' 'Each' LoopControlVariable 'In' LineTerminator? Expression StatementTerminator
      Block?
      ( 'Next' NextExpressionList? StatementTerminator )?
    ;

For...Next ステートメントと同じ規則に従います (Section For...次のステートメント)、ループ制御変数は、この For Each に固有の新しいローカル変数を参照します。次のステートメント、または既存の変数、または式。

列挙子式は値として分類する必要があり、その型はコレクション型または Objectである必要があります。 列挙子式の型が Object場合、すべての処理は実行時まで延期されます。 それ以外の場合は、コレクションの要素型からループ コントロール変数の型への変換が存在する必要があります。

ループ制御変数は、別の囲み For Each ステートメントでは使用できません。 For Eachステートメントは、一致する Next ステートメントによって閉じる必要があります。 ループ制御変数のない Next ステートメントは、最も内側の開いている For Eachと一致します。 1 つ以上のループ制御変数を持つ Next ステートメントは、左から右へ、同じループ制御変数を持つ For Each ループと一致します。 変数がその時点で最も入れ子になったループではない For Each ループと一致すると、コンパイル時エラーが発生します。

C型は、次のいずれかの場合にコレクション型と言われます。

  • 次のすべてが当てはまります。

    • Cには、E型を返すシグネチャ GetEnumerator()を持つ、アクセス可能なインスタンス、共有メソッド、または拡張メソッドが含まれています。
    • E には、シグネチャ MoveNext() と戻り値の型 Booleanを持つ、アクセス可能なインスタンス、共有メソッド、または拡張メソッドが含まれています。
    • E には、getter を持つ Current という名前のアクセス可能なインスタンスまたは共有プロパティが含まれています。 このプロパティの型は、コレクション型の要素型です。
  • インターフェイス System.Collections.Generic.IEnumerable(Of T)を実装します。この場合、コレクションの要素型は Tと見なされます。

  • インターフェイス System.Collections.IEnumerableを実装します。この場合、コレクションの要素型は Objectと見なされます。

列挙可能なクラスの例を次に示します。

Public Class IntegerCollection
    Private integers(10) As Integer

    Public Class IntegerCollectionEnumerator
        Private collection As IntegerCollection
        Private index As Integer = -1

        Friend Sub New(c As IntegerCollection)
            collection = c
        End Sub

        Public Function MoveNext() As Boolean
            index += 1

            Return index <= 10
        End Function

        Public ReadOnly Property Current As Integer
            Get
                If index < 0 OrElse index > 10 Then
                    Throw New System.InvalidOperationException()
                End If

                Return collection.integers(index)
            End Get
        End Property
    End Class

    Public Sub New()
        Dim i As Integer

        For i = 0 To 10
            integers(i) = I
        Next i
    End Sub

    Public Function GetEnumerator() As IntegerCollectionEnumerator
        Return New IntegerCollectionEnumerator(Me)
    End Function
End Class

ループが開始される前に、列挙子式が評価されます。 式の型がデザイン パターンを満たしていない場合、式は System.Collections.IEnumerable または System.Collections.Generic.IEnumerable(Of T)にキャストされます。 式の型がジェネリック インターフェイスを実装する場合は、コンパイル時にジェネリック インターフェイスが推奨されますが、実行時には非ジェネリック インターフェイスが推奨されます。 式の型がジェネリック インターフェイスを複数回実装する場合、ステートメントはあいまいと見なされ、コンパイル時エラーが発生します。

"注: ジェネリック インターフェイスを選択すると、インターフェイス メソッドのすべての呼び出しに型パラメーターが含まれることを意味するため、遅延バインドの場合は非ジェネリック インターフェイスが推奨されます。 実行時に一致する型引数を把握することはできないため、このような呼び出しはすべて遅延バインディング呼び出しを使用して行う必要があります。 コンパイル時の呼び出しを使用して非ジェネリック インターフェイスを呼び出すことができるため、これは非ジェネリック インターフェイスの呼び出しよりも遅くなります。

GetEnumerator は結果の値に対して呼び出され、関数の戻り値は一時に格納されます。 その後、各イテレーションの開始時に、一時 MoveNext が呼び出されます。 Falseを返すと、ループは終了します。 それ以外の場合、ループの各イテレーションは次のように実行されます。

  1. ループ制御変数が (既存のローカル変数ではなく) 新しいローカル変数を識別した場合は、このローカル変数の新しいコピーが作成されます。 現在のイテレーションでは、ループ ブロック内のすべての参照がこのコピーを参照します。
  2. Current プロパティが取得され、ループ コントロール変数の型に強制変換され (変換が暗黙的か明示的かに関係なく)、ループ コントロール変数に割り当てられます。
  3. ループ ブロックが実行されます。

"注: バージョン 10.0 と 11.0 の言語の動作に若干の変更があります。 11.0 より前では、ループの反復ごとに新しい反復変数が作成 されませんでした 。 この違いは、反復変数がラムダまたは LINQ 式によってキャプチャされ、ループの後に呼び出される場合にのみ観察できます。

Dim lambdas As New List(Of Action)
For Each x In {1,2,3}
   lambdas.Add(Sub() Console.WriteLine(x)
Next
lambdas(0).Invoke()
lambdas(1).Invoke()
lambdas(2).Invoke()

Visual Basic 10.0 までは、コンパイル時に警告が生成され、"3" が 3 回出力されました。 これは、ループのすべてのイテレーションで共有される変数 "x" が 1 つだけあり、3 つのラムダすべてが同じ "x" をキャプチャし、ラムダが実行された時点で数値 3 を保持していたためです。 Visual Basic 11.0 の時点では、"1、2、3" が出力されます。 これは、各ラムダが異なる変数 "x" をキャプチャするためです。

"注: ステートメントに変換演算子を導入する便利な場所がないため、変換が明示的な場合でも、反復処理の現在の要素はループ 制御変数の型に変換されます。 これは、要素型がObjectされているため、現在使用されなくなった型System.Collections.ArrayListを操作するときに特に面倒になりました。 これは非常に多くのループでキャストを必要としていましたが、私たちは理想的ではなかったと感じました。 皮肉なことに、ジェネリックは厳密に型指定されたコレクション ( System.Collections.Generic.List(Of T)) の作成を可能にしました。これにより、この設計ポイントが再考された可能性がありますが、互換性のために、これは今変更できません。

Next ステートメントに達すると、実行はループの先頭に戻ります。 Next キーワードの後に変数を指定する場合は、For Eachの後の最初の変数と同じである必要があります。 たとえば、次のコードを考えてみましょう。

Module Test
    Sub Main()
        Dim i As Integer
        Dim c As IntegerCollection = New IntegerCollection()

        For Each i In c
            Console.WriteLine(i)
        Next i
    End Sub
End Module

これは、次のコードと同じです。

Module Test
    Sub Main()
        Dim i As Integer
        Dim c As IntegerCollection = New IntegerCollection()

        Dim e As IntegerCollection.IntegerCollectionEnumerator

        e = c.GetEnumerator()
        While e.MoveNext()
            i = e.Current

            Console.WriteLine(i)
        End While
    End Sub
End Module

列挙子の型 ESystem.IDisposableを実装する場合、列挙子は、 Dispose メソッドを呼び出してループの終了時に破棄されます。 これにより、列挙子によって保持されているリソースが解放されます。 For Each ステートメントを含むメソッドが非構造化エラー処理を使用しない場合、For Each ステートメントは、Finallyで呼び出されたDispose メソッドを使用してTry ステートメントにラップされ、クリーンアップが保証されます。

"注: System.Array型はコレクション型であり、すべての配列型はSystem.Arrayから派生するため、For Each ステートメントでは任意の配列型式を使用できます。 1 次元配列の場合、 For Each ステートメントは、インデックス 0 から始まり、インデックス Length - 1 で終わる、インデックスの順序を増やして配列要素を列挙します。 多次元配列の場合、右端の次元のインデックスが最初に増加します。

たとえば、次のコードは 1 2 3 4を出力します。

Module Test
    Sub Main()
        Dim x(,) As Integer = { { 1, 2 }, { 3, 4 } }
        Dim i As Integer

        For Each i In x
            Console.Write(i & " ")
        Next i
    End Sub
End Module

ブロックの外側から For Each ステートメント ブロックに分岐することは無効です。

Exception-Handling ステートメント

Visual Basic では、構造化例外処理と非構造化例外処理がサポートされています。 メソッドで使用できる例外処理のスタイルは 1 つだけですが、構造化例外処理では Error ステートメントを使用できます。 メソッドで両方のスタイルの例外処理が使用されている場合、コンパイル時エラーが発生します。

ErrorHandlingStatement
    : StructuredErrorStatement
    | UnstructuredErrorStatement
    ;

構造化 Exception-Handling ステートメント

構造化例外処理は、特定の例外が処理される明示的なブロックを宣言することによってエラーを処理するメソッドです。 構造化例外処理は、 Try ステートメントを使用して実行されます。

StructuredErrorStatement
    : ThrowStatement
    | TryStatement
    ;

TryStatement
    : 'Try' StatementTerminator
      Block?
      CatchStatement*
      FinallyStatement?
      'End' 'Try' StatementTerminator
    ;

例えば次が挙げられます。

Module Test
    Sub ThrowException()
        Throw New Exception()
    End Sub

    Sub Main()
        Try
            ThrowException()
        Catch e As Exception
            Console.WriteLine("Caught exception!")
        Finally
            Console.WriteLine("Exiting try.")
        End Try
    End Sub
End Module

Try ステートメントは、try ブロック、catch ブロック、finally ブロックの 3 種類のブロックで構成されます。 try ブロックは、実行するステートメントを含むステートメント ブロックです。 catch ブロックは、例外を処理するステートメント ブロックです。 finally ブロックは、例外が発生して処理されたかどうかに関係なく、Try ステートメントが終了したときに実行されるステートメントを含むステートメント ブロックです。 1 つの try ブロックと 1 つの finally ブロックのみを含めることができる Try ステートメントには、少なくとも 1 つの catch ブロックまたは finally ブロックを含める必要があります。 同じステートメント内の catch ブロック内を除き、try ブロックに実行を明示的に転送することは無効です。

Finally ブロック

Finally ブロックは、Try ステートメントの任意の部分から実行が離れると常に実行されます。 Finally ブロックを実行するために明示的なアクションは必要ありません。実行が Try ステートメントから離れると、システムは自動的に Finally ブロックを実行し、実行を目的の宛先に転送します。 Finally ブロックは、実行が Try ステートメントから離れる方法 (Try ブロックの末尾、Catch ブロックの末尾、Exit Try ステートメント、GoTo ステートメント、またはスローされた例外を処理しないこと) に関係なく実行されます。

非同期メソッドの Await 式と反復子メソッドの Yield ステートメントによって、制御フローが非同期メソッドまたは反復子メソッド インスタンスで中断され、他のメソッド インスタンスで再開される可能性があることに注意してください。 ただし、これは単なる実行の中断であり、それぞれの非同期メソッドまたは反復子メソッド インスタンスを終了する必要がないため、 Finally ブロックは実行されません。

Finally ブロックに実行を明示的に転送することは無効です。例外を除き、Finally ブロックから実行を転送することも無効です。

FinallyStatement
    : 'Finally' StatementTerminator
      Block?
    ;

catch ブロック

Try ブロックの処理中に例外が発生した場合は、各Catchステートメントがテキスト順に調べられ、例外が処理されるかどうかを判断します。

CatchStatement
    : 'Catch' ( Identifier ( 'As' NonArrayTypeName )? )?
	  ( 'When' BooleanExpression )? StatementTerminator
      Block?
    ;

Catch句で指定された識別子は、スローされた例外を表します。 識別子に As 句が含まれている場合、識別子は Catch ブロックのローカル宣言空間内で宣言されていると見なされます。 それ以外の場合、識別子は、包含ブロックで定義されたローカル変数 (静的変数ではない) である必要があります。

識別子のない Catch 句は、 System.Exceptionから派生したすべての例外をキャッチします。 識別子を持つ Catch 句は、その型が識別子の型と同じか、その型から派生した例外のみをキャッチします。 型は System.Exception、または System.Exceptionから派生した型である必要があります。 System.Exceptionから派生した例外がキャッチされると、関数Microsoft.VisualBasic.Information.Errによって返されるオブジェクトに例外オブジェクトへの参照が格納されます。

When句を持つCatch句は、式がTrueに評価された場合にのみ例外をキャッチします。式の型は、セクションブール式に従ってブールである必要があります。 When句は、例外の型を確認した後にのみ適用され、この例で示すように、式は例外を表す識別子を参照できます。

Module Test
    Sub Main()
        Dim i As Integer = 5

        Try
            Throw New ArgumentException()
        Catch e As OverflowException When i = 5
            Console.WriteLine("First handler")
        Catch e As ArgumentException When i = 4
            Console.WriteLine("Second handler")
        Catch When i = 5
            Console.WriteLine("Third handler")
        End Try

    End Sub
End Module

この例では、次の内容が出力されます。

Third handler

Catch句が例外を処理する場合、実行は Catch ブロックに転送されます。 Catch ブロックの最後で、Try ステートメントの後の最初のステートメントに実行が転送されます。 Try ステートメントは、Catch ブロックでスローされた例外を処理しません。 例外を処理 Catch 句がない場合、実行はシステムによって決定された場所に転送されます。

Catch ブロックに実行を明示的に転送することは無効です。

通常、When 句のフィルターは、例外がスローされる前に評価されます。 たとえば、次のコードは "Filter, Finally, Catch" を出力します。

Sub Main()
   Try
       Foo()
   Catch ex As Exception When F()
       Console.WriteLine("Catch")
   End Try
End Sub

Sub Foo()
    Try
        Throw New Exception
    Finally
        Console.WriteLine("Finally")
    End Try
End Sub

Function F() As Boolean
    Console.WriteLine("Filter")
    Return True
End Function

ただし、Async メソッドと Iterator メソッドを使用すると、外部のフィルターの前に、その内部のすべてのブロックが最終的に実行されます。 たとえば、上記のコードに Async Sub Foo()がある場合、出力は "Finally, Filter, Catch" になります。

Throw ステートメント

Throw ステートメントは例外を発生させます。例外は、System.Exceptionから派生した型のインスタンスによって表されます。

ThrowStatement
    : 'Throw' Expression? StatementTerminator
    ;

式が値として分類されていないか、 System.Exceptionから派生した型でない場合は、コンパイル時エラーが発生します。 実行時に式が null 値に評価された場合は、代わりに System.NullReferenceException 例外が発生します。

Throw ステートメントは、finally ブロックが介在しない限り、Try ステートメントの catch ブロック内の式を省略できます。 その場合、ステートメントは catch ブロック内で現在処理されている例外を再スローします。 例えば次が挙げられます。

Sub Test(x As Integer)
    Try
        Throw New Exception()
    Catch
        If x = 0 Then
            Throw    ' OK, rethrows exception from above.
        Else
            Try
                If x = 1 Then
                    Throw   ' OK, rethrows exception from above.
                End If
            Finally
                Throw    ' Invalid, inside of a Finally.
            End Try
        End If
    End Try
End Sub

非構造化 Exception-Handling ステートメント

非構造化例外処理は、例外が発生したときに分岐するステートメントを示すことによってエラーを処理する方法です。 非構造化例外処理は、 Error ステートメント、 On Error ステートメント、および Resume ステートメントの 3 つのステートメントを使用して実装されます。

UnstructuredErrorStatement
    : ErrorStatement
    | OnErrorStatement
    | ResumeStatement
    ;

例えば次が挙げられます。

Module Test
    Sub ThrowException()
        Error 5
    End Sub

    Sub Main()
        On Error GoTo GotException

        ThrowException()
        Exit Sub

GotException:
        Console.WriteLine("Caught exception!")
        Resume Next
    End Sub
End Module

メソッドが非構造化例外処理を使用する場合、すべての例外をキャッチするメソッド全体に対して 1 つの構造化例外ハンドラーが確立されます。 (コンストラクターでは、このハンドラーはコンストラクターの先頭で New 呼び出しの呼び出しに対して拡張されないことに注意してください)。その後、メソッドは、最新の例外ハンドラーの場所と、スローされた最新の例外を追跡します。 メソッドへのエントリでは、例外ハンドラーの場所と例外の両方が Nothingに設定されます。 非構造化例外処理を使用するメソッドで例外がスローされると、関数 Microsoft.VisualBasic.Information.Errによって返されるオブジェクトに例外オブジェクトへの参照が格納されます。

非構造化エラー処理ステートメントは、反復子または非同期メソッドでは使用できません。

Error ステートメント

Error ステートメントは、Visual Basic 6 例外番号を含むSystem.Exception例外をスローします。 式は値として分類する必要があり、その型は暗黙的に Integerに変換できる必要があります。

ErrorStatement
    : 'Error' Expression StatementTerminator
    ;

On Error ステートメント

On Error ステートメントは、最新の例外処理状態を変更します。

OnErrorStatement
    : 'On' 'Error' ErrorClause StatementTerminator
    ;

ErrorClause
    : 'GoTo' '-' '1'
    | 'GoTo' '0'
    | GoToStatement
    | 'Resume' 'Next'
    ;

次の 4 つの方法のいずれかで使用できます。

  • On Error GoTo -1 は、最新の例外を Nothingにリセットします。

  • On Error GoTo 0 は、最新の例外ハンドラーの場所を Nothingにリセットします。

  • On Error GoTo LabelName は、ラベルを最新の例外ハンドラーの場所として確立します。 このステートメントは、ラムダ式またはクエリ式を含むメソッドでは使用できません。

  • On Error Resume Next は、 Resume Next 動作を最新の例外ハンドラーの場所として確立します。

Resume ステートメント

Resume ステートメントは、最新の例外の原因となったステートメントに実行を返します。

ResumeStatement
    : 'Resume' ResumeClause? StatementTerminator
    ;

ResumeClause
    : 'Next'
    | LabelName
    ;

Next修飾子が指定されている場合、実行は、最新の例外の原因となったステートメントの後に実行されたステートメントに戻ります。 ラベル名が指定されている場合、実行はラベルに戻ります。

SyncLock ステートメントには暗黙的な構造化エラー処理ブロックが含まれているため、ResumeおよびResume Nextには、SyncLock ステートメントで発生する例外に対して特別な動作があります。 ResumeSyncLock ステートメントの先頭に実行を返し、 Resume NextSyncLock ステートメントの後の次のステートメントに実行を返します。 たとえば、次のコードを考えてみましょう。

Class LockClass
End Class

Module Test
    Sub Main()
        Dim FirstTime As Boolean = True
        Dim Lock As LockClass = New LockClass()

        On Error GoTo Handler

        SyncLock Lock
            Console.WriteLine("Before exception")
            Throw New Exception()
            Console.WriteLine("After exception")
        End SyncLock

        Console.WriteLine("After SyncLock")
        Exit Sub

Handler:
        If FirstTime Then
            FirstTime = False
            Resume
        Else
            Resume Next
        End If
    End Sub
End Module

次の結果が出力されます。

Before exception
Before exception
After SyncLock

SyncLock ステートメントを初めて使用すると、ResumeSyncLock ステートメントの先頭に実行を返します。 SyncLock ステートメントを 2 回目に実行Resume NextSyncLock ステートメントの末尾に戻ります。 Resume および Resume Next は、 SyncLock ステートメント内では使用できません。

いずれの場合も、 Resume ステートメントが実行されると、最新の例外が Nothing に設定されます。 Resume ステートメントが最新の例外なしで実行された場合、Visual Basic エラー番号20 (エラーなしで再開) を含むSystem.Exception例外が発生します。

Branch ステートメント

Branch ステートメントは、メソッド内の実行フローを変更します。 次の 6 つの分岐ステートメントがあります。

  1. GoTo ステートメントを実行すると、メソッド内の指定されたラベルに転送されます。 TryUsingSyncLockWithFor、またはFor EachブロックにGoToしたり、そのブロックのローカル変数がラムダまたは LINQ 式でキャプチャされた場合は、ループ ブロックにすることはできません。
  2. Exit ステートメントは、指定した種類のブロック ステートメントをすぐに含むステートメントが終了した後、次のステートメントに実行を転送します。 ブロックがメソッド ブロックの場合、制御フローはセクション 制御フローの説明に従ってメソッドを終了します。 Exit ステートメントがステートメントで指定されたブロックの種類に含まれていない場合は、コンパイル時エラーが発生します。
  3. Continue ステートメントは、指定した種類のブロック ループ ステートメントをすぐに含むステートメントの末尾に実行を転送します。 Continue ステートメントがステートメントで指定されたブロックの種類に含まれていない場合は、コンパイル時エラーが発生します。
  4. Stop ステートメントを実行すると、デバッガーの例外が発生します。
  5. End ステートメントは、プログラムを終了します。 ファイナライザーはシャットダウン前に実行されますが、現在実行中の Try ステートメントの finally ブロックは実行されません。 このステートメントは、実行可能でないプログラム (DLL など) では使用できません。
  6. 式のない Return ステートメントは、 Exit Sub または Exit Function ステートメントと同じです。 式を含むReturnステートメントは、関数である通常のメソッド、または一部のTの戻り値の型Task(Of T)を持つ関数である非同期メソッドでのみ使用できます。 式は、 関数の戻り値変数 (通常のメソッドの場合) または タスクの戻り値変数 (非同期メソッドの場合) に暗黙的に変換できる値として分類する必要があります。 その動作は、式を評価し、それを戻り変数に格納してから、暗黙的な Exit Function ステートメントを実行することです。
BranchStatement
    : GoToStatement
    | ExitStatement
    | ContinueStatement
    | StopStatement
    | EndStatement
    | ReturnStatement
    ;

GoToStatement
    : 'GoTo' LabelName StatementTerminator
    ;

ExitStatement
    : 'Exit' ExitKind StatementTerminator
    ;

ExitKind
    : 'Do' | 'For' | 'While' | 'Select' | 'Sub' | 'Function' | 'Property' | 'Try'
    ;

ContinueStatement
    : 'Continue' ContinueKind StatementTerminator
    ;

ContinueKind
    : 'Do' | 'For' | 'While'
    ;

StopStatement
    : 'Stop' StatementTerminator
    ;

EndStatement
    : 'End' StatementTerminator
    ;

ReturnStatement
    : 'Return' Expression? StatementTerminator
    ;

Array-Handling ステートメント

ReDim ステートメントと Erase ステートメントという 2 つのステートメントを使用すると、配列の操作が簡単になります。

ArrayHandlingStatement
    : RedimStatement
    | EraseStatement
    ;

ReDim ステートメント

ReDim ステートメントは、新しい配列をインスタンス化します。

RedimStatement
    : 'ReDim' 'Preserve'? RedimClauses StatementTerminator
    ;

RedimClauses
    : RedimClause ( Comma RedimClause )*
    ;

RedimClause
    : Expression ArraySizeInitializationModifier
    ;

ステートメント内の各句は、型が配列型または Objectである変数またはプロパティ アクセスとして分類され、その後に配列の境界の一覧が続く必要があります。 境界の数は、変数の型と一致している必要があります。 Objectでは、任意の数の境界が許可されます。 実行時に、指定された境界を持つ左から右の各式の配列がインスタンス化され、変数またはプロパティに割り当てられます。 変数型が Objectの場合、次元の数は指定された次元の数であり、配列要素の型は Object。 指定された数のディメンションが実行時に変数またはプロパティと互換性がない場合は、コンパイル時エラーが発生します。 例えば次が挙げられます。

Module Test
    Sub Main()
        Dim o As Object
        Dim b() As Byte
        Dim i(,) As Integer

        ' The next two statements are equivalent.
        ReDim o(10,30)
        o = New Object(10, 30) {}

        ' The next two statements are equivalent.
        ReDim b(10)
        b = New Byte(10) {}

        ' Error: Incorrect number of dimensions.
        ReDim i(10, 30, 40)
    End Sub
End Module

Preserve キーワードを指定する場合、式は値としても分類でき、右端を除く各次元の新しいサイズは、既存の配列のサイズと同じである必要があります。 既存の配列の値は新しい配列にコピーされます。新しい配列が小さい場合、既存の値は破棄されます。新しい配列が大きい場合、追加の要素は配列の要素型の既定値に初期化されます。 たとえば、次のコードを考えてみましょう。

Module Test
    Sub Main()
        Dim x(5, 5) As Integer

        x(3, 3) = 3

        ReDim Preserve x(5, 6)
        Console.WriteLine(x(3, 3) & ", " & x(3, 6))
    End Sub
End Module

次の結果が出力されます。

3, 0

既存の配列参照が実行時に null 値である場合、エラーは発生しません。 右端のディメンション以外では、ディメンションのサイズが変更されると、 System.ArrayTypeMismatchException がスローされます。

"注: Preserve は予約語ではありません。

Erase ステートメント

Erase ステートメントは、ステートメントで指定された配列変数またはプロパティをそれぞれNothingに設定します。 ステートメント内の各式は、型が配列型または Objectである変数またはプロパティ アクセスとして分類する必要があります。 例えば次が挙げられます。

Module Test
    Sub Main()
        Dim x() As Integer = New Integer(5) {}

        ' The following two statements are equivalent.
        Erase x
        x = Nothing
    End Sub
End Module
EraseStatement
    : 'Erase' EraseExpressions StatementTerminator
    ;

EraseExpressions
    : Expression ( Comma Expression )*
    ;

Using ステートメント

コレクションが実行され、インスタンスへのライブ参照が見つからない場合、型のインスタンスはガベージ コレクターによって自動的に解放されます。 型が特に貴重で希少なリソース (データベース接続やファイル ハンドルなど) を保持している場合は、次のガベージ コレクションが使用されなくなった型の特定のインスタンスをクリーンアップするまで待つのが望ましくない場合があります。 コレクションの前にリソースを解放する軽量な方法を提供するために、型は System.IDisposable インターフェイスを実装できます。 そうする型は、貴重なリソースをすぐに解放するように強制するために呼び出すことができる Dispose メソッドを公開します。

Module Test
    Sub Main()
        Dim x As DBConnection = New DBConnection("...")

        ' Do some work
        ...

        x.Dispose()        ' Free the connection
    End Sub
End Module

Using ステートメントは、リソースの取得、一連のステートメントの実行、リソースの破棄のプロセスを自動化します。 ステートメントは 2 つの形式をとることができます。1 つ目のリソースは、ステートメントの一部として宣言され、通常のローカル変数宣言ステートメントとして扱われるローカル変数です。もう一方では、リソースは式の結果です。

UsingStatement
    : 'Using' UsingResources StatementTerminator
      Block?
      'End' 'Using' StatementTerminator
    ;

UsingResources
    : VariableDeclarators
    | Expression
    ;

リソースがローカル変数宣言ステートメントの場合、ローカル変数宣言の型は、暗黙的に System.IDisposableに変換できる型である必要があります。 宣言されたローカル変数は読み取り専用で、 Using ステートメント ブロックをスコープとし、初期化子を含める必要があります。 リソースが式の結果である場合、式は値として分類され、暗黙的に System.IDisposableに変換できる型である必要があります。 式は、ステートメントの先頭で 1 回だけ評価されます。

Using ブロックは、リソースにIDisposable.Disposeメソッドを最後にブロック呼び出すTry ステートメントによって暗黙的に含まれます。 これにより、例外がスローされた場合でもリソースが破棄されます。 その結果、ブロックの外部から Using ブロックに分岐することは無効であり、 Using ブロックは、 ResumeResume Nextの目的で単一のステートメントとして扱われます。 リソースが Nothingされている場合、 Dispose の呼び出しは行われません。 したがって、次の例を示します。

Using f As C = New C()
    ...
End Using

〜に相当します。

Dim f As C = New C()
Try
    ...
Finally
    If f IsNot Nothing Then
        f.Dispose()
    End If
End Try

ローカル変数宣言ステートメントを持つ Using ステートメントは、入れ子になった Using ステートメントと同等の複数のリソースを一度に取得できます。 たとえば、次の形式の Using ステートメントです。

Using r1 As R = New R(), r2 As R = New R()
    r1.F()
    r2.F()
End Using

〜に相当します。

Using r1 As R = New R()
    Using r2 As R = New R()
        r1.F()
        r2.F()
    End Using
End Using

Await ステートメント

await ステートメントは await 演算子式 (Section Await 演算子) と同じ構文を持ち、await 式も許可し、await 演算子式と同じ動作を持つメソッドでのみ使用できます。

ただし、値または void として分類される場合があります。 await 演算子式の評価に起因するすべての値は破棄されます。

AwaitStatement
    : AwaitOperatorExpression StatementTerminator
    ;

Yield ステートメント

Yield ステートメントは、セクション反復子メソッドで説明されている反復子メソッドに関連 しています

YieldStatement
    : 'Yield' Expression StatementTerminator
    ;

Yieldは、即時に囲むメソッドまたはラムダ式にIterator修飾子があり、そのIterator修飾子の後にYieldが表示される場合は予約語です。他の場所では予約されません。 プリプロセッサ ディレクティブでも予約されません。 yield ステートメントは、予約語であるメソッドまたはラムダ式の本体でのみ使用できます。 すぐに囲むメソッドまたはラムダ内では、yield ステートメントは、 Catch または Finally ブロックの本体内、または SyncLock ステートメントの本体内に含まれていない可能性があります。

yield ステートメントは、値として分類する必要があり、その型が外側の反復子メソッドの 反復子の現在の変数 (セクション 反復子メソッド) の型に暗黙的に変換できる単一の式を受け取ります。

制御フローは、反復子オブジェクトに対してMoveNext メソッドが呼び出された場合にのみ、Yield ステートメントに到達します。 (これは、反復子メソッド インスタンスは、反復子オブジェクトで呼び出されるMoveNextまたはDisposeメソッドによってのみステートメントを実行するためです。Dispose メソッドは、Yieldが許可されていないFinally ブロック内でのみコードを実行するためです)。

Yield ステートメントが実行されると、その式が評価され、その反復子オブジェクトに関連付けられている反復子メソッド インスタンスの反復子の現在の変数に格納されます。 True値はMoveNextの呼び出し側に返され、このインスタンスの制御ポイントは、反復子オブジェクトに対するMoveNextの次の呼び出しまで進行を停止します。