Try...Catch...Finally ステートメント (Visual Basic)

コード内の所定のブロックで発生する可能性のあるエラーの一部またはすべてに対して、コードを実行しながらエラー処理を実行できます。

構文

Try
    [ tryStatements ]
    [ Exit Try ]
[ Catch [ exception [ As type ] ] [ When expression ]
    [ catchStatements ]
    [ Exit Try ] ]
[ Catch ... ]
[ Finally
    [ finallyStatements ] ]
End Try

指定項目

用語 定義
tryStatements 任意。 エラーが発生する可能性のあるステートメント。 複合ステートメントにすることもできます。
Catch 任意。 複数の Catch ブロックが許可されています。 Try ブロックの処理中に例外が発生した場合、各 Catch ステートメントは、スローされた例外を表す exception を使用して、例外を処理するかどうかを判断するためにテキスト順に検証されます。
exception 任意。 任意の変数名を指定します。 exception の初期値は、スローされたエラーの値です。 キャッチされたエラーを指定するために Catch と共に使用します。 省略した場合は、Catch ステートメントが例外をキャッチします。
type 任意。 クラス フィルターの型を指定します。 exception の値が、type によって指定された型の値、または派生型の値の場合、識別子は例外オブジェクトにバインドされます。
When 任意。 When 句を含む Catch ステートメントは、expressionTrue に評価される場合にのみ例外をキャッチします。 When 句は、例外の型をチェックした後にのみ適用され、expression は例外を表す識別子を参照する場合があります。
expression 任意。 暗黙的に Boolean に変換される必要があります。 汎用フィルターを記述する任意の式。 通常、エラー番号でフィルター処理するために使用されます。 When キーワードと共に使用して、エラーがキャッチされる状況を指定します。
catchStatements 任意。 関連付けられた Try ブロックで発生するエラーを処理するステートメント。 複合ステートメントにすることもできます。
Exit Try 任意。 Try...Catch...Finally 構造体から抜け出すキーワード。 実行は、End Try ステートメントの直後のコードで再開されます。 Finally ステートメントは引き続き実行されます。 Finally ブロックでは許可されていません。
Finally 任意。 Finally ブロックは、実行が Try...Catch ステートメントの任意の部分から離れたときに常に実行されます。
finallyStatements 任意。 他のエラー処理がすべて実行された後で実行されるステートメント。
End Try Try...Catch...Finally 構造体を終了します。

Remarks

特定の例外がコードの特定のセクションで発生することが予想される場合は、コードを Try ブロックに配置し、Catch ブロックを使用して制御を維持し、例外が発生した場合は処理します。

Try…Catch ステートメントは、Try ブロックと、それに続く 1 つ以上の Catch 句で構成されます。この句にはさまざまな例外のハンドラーを指定します。 Try ブロックで例外がスローされると、Visual Basic では、この例外を処理する Catch ステートメントが検索されます。 一致する Catch ステートメントが見つからない場合、Visual Basic は、現在のメソッドを呼び出したメソッドを調べ、呼び出し履歴を調べていきます。 Catch ブロックが見つからない場合、Visual Basic は、ハンドルされない例外のメッセージをユーザーに表示し、プログラムの実行を停止します。

Try…Catch ステートメント内で複数の Catch ステートメントを使用できます。 これを実行すると、Catch 句は順序どおりにチェックされるため、順序が重要になります。 例外は、特殊性の高い順にキャッチしてください。

次の Catch ステートメントの条件は最も限定的ではなく、Exception クラスから派生したすべての例外をキャッチします。 通常は、予期したすべての具体的な例外をキャッチした後、これらのバリエーションのいずれかを Try...Catch...Finally 構造体の最後の Catch ブロックとして使用してください。 制御フローは、次のいずれかのバリエーションに従う Catch ブロックに達することはできません。

  • typeException である (例: Catch ex As Exception)

  • ステートメントに exception 変数がない (例: Catch)

Try…Catch…Finally ステートメントが別の Try ブロック内に入れ子になっている場合、Visual Basic はまず、最も内側の Try ブロック内の各 Catch ステートメントを調べます。 一致する Catch ステートメントが見つからない場合、検索は外側の Try…Catch…Finally ブロックの Catch ステートメントに進みます。

Try ブロックのローカル変数は、個別のブロックであるため、Catch ブロックでは使用できません。 複数のブロックで変数を使用する場合は、Try...Catch...Finally 構造体の外側で変数を宣言します。

ヒント

Try…Catch…Finally ステートメントは、IntelliSense コード スニペットとして利用できます。 コード スニペット マネージャーで、 [コード パターン - If、For Each、Try Catch、Property、その他] を展開してから、 [エラー処理 (例外)] を展開します。 詳細については、「Code Snippets」を参照してください。

Finally ブロック

Try 構造体を終了する前に実行する必要のあるステートメントが 1 つ以上ある場合は、Finally ブロックを使用します。 制御は、Try…Catch 構造体から渡される直前に Finally ブロックに渡されます。 これは、Try 構造体の内部のどこかで例外が発生した場合でも当てはまります。

Finally ブロックは、例外が発生した場合でも実行する必要があるコードを実行する場合に便利です。 制御は、Try...Catch ブロックの終了方法に関係なく、Finally ブロックに渡されます。

Finally ブロック内のコードは、コードが Try または Catch ブロックで Return ステートメントを検出した場合でも実行されます。 次の場合、制御は、Try または Catch ブロックから対応する Finally ブロックに渡されません。

実行を明示的に Finally ブロックに移動することは有効ではありません。 例外を除き、Finally ブロックからの実行を移動することは有効ではありません。

Try ステートメントに少なくとも 1 つの Catch ブロックが含まれていない場合は、Finally ブロックが含まれている必要があります。

ヒント

特定の例外をキャッチする必要がない場合、Using ステートメントは、ブロックを終了する方法に関係なく、Try…Finally ブロックのように動作し、リソースの破棄を保証します。 これは、ハンドルされない例外が発生した場合でも当てはまります。 詳細については、「Using ステートメント」を参照してください。

Exception 引数

Catch ブロックの exception 引数は、Exception クラスのインスタンス、または Exception クラスから派生したクラスです。 Exception クラス インスタンスは、Try ブロックで発生したエラーに対応します。

Exception オブジェクトのプロパティは、例外の原因と場所を特定するのに役立ちます。 たとえば、StackTrace プロパティは、例外の原因となった呼び出し元のメソッドを一覧表示し、コード内でエラーが発生した場所を見つけるのに役立ちます。 Message は、例外について説明するメッセージを返します。 HelpLink は、関連付けられたヘルプ ファイルへのリンクを返します。 InnerException は、現在の例外の原因となった Exception オブジェクトを返します。元の Exception がない場合は Nothing を返します。

Try…Catch ステートメントを使用する場合の考慮事項

Try…Catch ステートメントは、異常な、または予期しないプログラム イベントの発生を通知するためにのみ使用します。 これには、次のような理由があります。

  • 実行時に例外をキャッチするとオーバーヘッドが増加するため、例外を回避するための事前チェックよりも低速になる可能性がある。

  • Catch ブロックが正しく処理されない場合は、例外がユーザーに正しく報告されない可能性がある。

  • 例外処理により、プログラムがより複雑になる。

発生する可能性がある条件をチェックするために、必ずしも Try…Catch ステートメントは必要ありません。 次の例では、ファイルを開こうとする前に、そのファイルが存在するかどうかを確認します。 これにより、OpenText メソッドによってスローされた例外をキャッチする必要がなくなります。

Private Sub TextFileExample(ByVal filePath As String)

    ' Verify that the file exists.
    If System.IO.File.Exists(filePath) = False Then
        Console.Write("File Not Found: " & filePath)
    Else
        ' Open the text file and display its contents.
        Dim sr As System.IO.StreamReader =
            System.IO.File.OpenText(filePath)

        Console.Write(sr.ReadToEnd)

        sr.Close()
    End If
End Sub

スレッドセーフなログ記録と適切なメッセージのどちらを使用しても、Catch ブロック内のコードでユーザーに例外を適切に報告できることを確認します。 そうでない場合、例外は不明なままになる可能性があります。

非同期メソッド

メソッドに Async 修飾子を付けると、そのメソッドで Await 演算子を使用できます。 Await 演算子を含むステートメントは、待機しているタスクが完了するまでメソッドの実行を中断します。 このタスクは、進行中の作業を表します。 Await 演算子に関連付けられているタスクが終了すると、実行は同じメソッド内で再開されます。 詳細については、「非同期プログラムにおける制御フロー」を参照してください。

非同期メソッドによって返されるタスクは、ハンドルされない例外が原因で完了したことを示す違反状態で終了することがあります。 また、タスクは取り消された状態で終了することもあります。その結果、await 式から OperationCanceledException がスローされます。 いずれかの種類の例外をキャッチするには、タスクに関連付けられている Await 式を Try ブロックに配置し、Catch ブロックで例外をキャッチします。 例については、このトピックで後述します。

複数の例外が違反の原因になっているため、タスクが違反状態になることがあります。 たとえば、タスクは Task.WhenAll の呼び出しの結果になることがあります。 このようなタスクを待機した場合、キャッチされた例外は例外の 1 つにすぎず、どの例外がキャッチされるかは予測できません。 例については、このトピックで後述します。

Await 式を Catch ブロックや Finally ブロックに記述することはできません。

Iterators

iterator 関数または Get アクセサーは、コレクションに対するカスタムの反復を実行します。 反復子は、Yield ステートメントを使用して、コレクションの各要素を 1 回に 1 つ返します。 For Each...Next ステートメントを使用して、iterator 関数を呼び出します。

Yield ステートメントは Try ブロック内に記述できます。 Yield ステートメントが含まれている Try ブロックには、Catch ブロックと Finally ブロックを記述することができます。 例については、「反復子」の「Visual Basic の Try ブロック」セクションを参照してください。

Yield ステートメントを Catch ブロックや Finally ブロックに記述することはできません。

(iterator 関数の外部の) For Each 本体で例外がスローされた場合、iterator 関数の Catch ブロックは実行されず、iterator 関数の Finally ブロックが実行されます。 iterator 関数内の Catch ブロックでキャッチされるのは、iterator 関数内で発生した例外だけです。

部分信頼の状況

部分信頼の状況 (ネットワーク共有でホストされているアプリケーションなど) では、Try...Catch...Finally は、呼び出しを含むメソッドが呼び出される前に発生したセキュリティ例外をキャッチしません。 次の例では、サーバー共有に配置し、そこから実行すると、エラー "System.Security.SecurityException:要求が失敗しました。" が生成されます。セキュリティ例外について詳しくは、SecurityException クラスをご覧ください。

Try
    Process.Start("http://www.microsoft.com")
Catch ex As Exception
    MsgBox("Can't load Web page" & vbCrLf & ex.Message)
End Try

このような部分信頼では、Process.Start ステートメントを別の Sub に配置する必要があります。 Sub の最初の呼び出しは失敗します。 これにより、Process.Start を含む Sub が開始されてセキュリティ例外が生成される前に、Try...Catch をキャッチできます。

使用例

Try...Catch...Finally の構造

次の例は、Try...Catch...Finally ステートメントの構造を示しています。

Public Sub TryExample()
    ' Declare variables.
    Dim x As Integer = 5
    Dim y As Integer = 0

    ' Set up structured error handling.
    Try
        ' Cause a "Divide by Zero" exception.
        x = x \ y

        ' This statement does not execute because program
        ' control passes to the Catch block when the
        ' exception occurs.
        MessageBox.Show("end of Try block")
    Catch ex As Exception
        ' Show the exception's message.
        MessageBox.Show(ex.Message)

        ' Show the stack trace, which is a list of methods
        ' that are currently executing.
        MessageBox.Show("Stack Trace: " & vbCrLf & ex.StackTrace)
    Finally
        ' This line executes whether or not the exception occurs.
        MessageBox.Show("in Finally block")
    End Try
End Sub

Try ブロックから呼び出されたメソッドでの例外

次の例では、CreateException メソッドは NullReferenceException をスローします。 例外を生成するコードが Try ブロックに含まれていません。 したがって、CreateException メソッドは例外を処理しません。 RunSample メソッドは、CreateException メソッドの呼び出しが Try ブロック内にあるため、例外を処理します。

この例には、いくつかの種類の例外の Catch ステートメントが含まれており、最も具体的なものから順に示されています。

Public Sub RunSample()
    Try
        CreateException()
    Catch ex As System.IO.IOException
        ' Code that reacts to IOException.
    Catch ex As NullReferenceException
        MessageBox.Show("NullReferenceException: " & ex.Message)
        MessageBox.Show("Stack Trace: " & vbCrLf & ex.StackTrace)
    Catch ex As Exception
        ' Code that reacts to any other exception.
    End Try
End Sub

Private Sub CreateException()
    ' This code throws a NullReferenceException.
    Dim obj = Nothing
    Dim prop = obj.Name

    ' This code also throws a NullReferenceException.
    'Throw New NullReferenceException("Something happened.")
End Sub

Catch When ステートメント

次の例では、Catch When ステートメントを使用して条件式をフィルター処理する方法を示します。 条件式が True と評価された場合、Catch ブロック内のコードが実行されます。

Private Sub WhenExample()
    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
' Output: Third handler

入れ子になった Try ステートメント

次の例では、Try ブロックに含まれる Try…Catch ステートメントが含まれます。 内側の Catch ブロックは、その InnerException プロパティが元の例外に設定されている例外をスローします。 外側の Catch ブロックは、独自の例外と内部例外を報告します。

Private Sub InnerExceptionExample()
    Try
        Try
            ' Set a reference to a StringBuilder.
            ' The exception below does not occur if the commented
            ' out statement is used instead.
            Dim sb As System.Text.StringBuilder
            'Dim sb As New System.Text.StringBuilder

            ' Cause a NullReferenceException.
            sb.Append("text")
        Catch ex As Exception
            ' Throw a new exception that has the inner exception
            ' set to the original exception.
            Throw New ApplicationException("Something happened :(", ex)
        End Try
    Catch ex2 As Exception
        ' Show the exception.
        Console.WriteLine("Exception: " & ex2.Message)
        Console.WriteLine(ex2.StackTrace)

        ' Show the inner exception, if one is present.
        If ex2.InnerException IsNot Nothing Then
            Console.WriteLine("Inner Exception: " & ex2.InnerException.Message)
            Console.WriteLine(ex2.StackTrace)
        End If
    End Try
End Sub

非同期メソッドの例外処理

次の例では、非同期メソッドの例外処理を示します。 非同期タスクに適用される例外をキャッチするために、Await 式は呼び出し元の Try ブロックにあり、例外は Catch ブロックでキャッチされます。

例外処理を示すために、この例の Throw New Exception 行のコメントを解除します。 例外が Catch ブロックでキャッチされ、タスクの IsFaulted プロパティが True に設定され、タスクの Exception.InnerException プロパティが例外に設定されます。

Throw New OperationCancelledException 行のコメントを解除して、非同期処理を取り消したときに何が起こるかを示します。 例外が True ブロックでキャッチされ、タスクの Catch プロパティが IsCanceled に設定されます。 ただし、この例に該当しない一部の条件では、IsFaultedTrue に設定され、IsCanceledFalse に設定されます。

Public Async Function DoSomethingAsync() As Task
    Dim theTask As Task(Of String) = DelayAsync()

    Try
        Dim result As String = Await theTask
        Debug.WriteLine("Result: " & result)
    Catch ex As Exception
        Debug.WriteLine("Exception Message: " & ex.Message)
    End Try

    Debug.WriteLine("Task IsCanceled: " & theTask.IsCanceled)
    Debug.WriteLine("Task IsFaulted:  " & theTask.IsFaulted)
    If theTask.Exception IsNot Nothing Then
        Debug.WriteLine("Task Exception Message: " &
            theTask.Exception.Message)
        Debug.WriteLine("Task Inner Exception Message: " &
            theTask.Exception.InnerException.Message)
    End If
End Function

Private Async Function DelayAsync() As Task(Of String)
    Await Task.Delay(100)

    ' Uncomment each of the following lines to
    ' demonstrate exception handling.

    'Throw New OperationCanceledException("canceled")
    'Throw New Exception("Something happened.")
    Return "Done"
End Function


' Output when no exception is thrown in the awaited method:
'   Result: Done
'   Task IsCanceled: False
'   Task IsFaulted:  False

' Output when an Exception is thrown in the awaited method:
'   Exception Message: Something happened.
'   Task IsCanceled: False
'   Task IsFaulted:  True
'   Task Exception Message: One or more errors occurred.
'   Task Inner Exception Message: Something happened.

' Output when an OperationCanceledException or TaskCanceledException
' is thrown in the awaited method:
'   Exception Message: canceled
'   Task IsCanceled: True
'   Task IsFaulted:  False

非同期メソッドの複数の例外の処理

次の例では、複数のタスクで複数の例外が発生する可能性がある例外処理について説明します。 Try ブロックには、Task.WhenAll で返されたタスクの Await 式が含まれています。 Task.WhenAll が適用される 3 つのタスクが完了すると、このタスクは完了します。

3 つのタスクでそれぞれ例外が発生します。 Catch ブロックは例外を反復処理します。この例外は、Task.WhenAll が返したタスクの Exception.InnerExceptions プロパティで見つかります。

Public Async Function DoMultipleAsync() As Task
    Dim theTask1 As Task = ExcAsync(info:="First Task")
    Dim theTask2 As Task = ExcAsync(info:="Second Task")
    Dim theTask3 As Task = ExcAsync(info:="Third Task")

    Dim allTasks As Task = Task.WhenAll(theTask1, theTask2, theTask3)

    Try
        Await allTasks
    Catch ex As Exception
        Debug.WriteLine("Exception: " & ex.Message)
        Debug.WriteLine("Task IsFaulted: " & allTasks.IsFaulted)
        For Each inEx In allTasks.Exception.InnerExceptions
            Debug.WriteLine("Task Inner Exception: " + inEx.Message)
        Next
    End Try
End Function

Private Async Function ExcAsync(info As String) As Task
    Await Task.Delay(100)

    Throw New Exception("Error-" & info)
End Function

' Output:
'   Exception: Error-First Task
'   Task IsFaulted: True
'   Task Inner Exception: Error-First Task
'   Task Inner Exception: Error-Second Task
'   Task Inner Exception: Error-Third Task

関連項目