分享方式:


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 選擇性。 只有在 expression 評估為 True 時,具有 When 子句的 Catch 陳述式才會攔截例外狀況。 僅在檢查例外狀況的類型之後,才能套用 When 子句,而 expression 可以參考代表例外狀況的識別碼。
expression 選擇性。 必須隱含轉換為 Boolean。 描述一般篩選的任何運算式。 通常用於按錯誤號碼進行篩選。 與 When 關鍵字搭配使用,以指定攔截錯誤的情況。
catchStatements 選擇性。 用於處理相關聯 Try 區塊中所發生錯誤的陳述式。 可以是複合陳述式。
Exit Try 選擇性。 中斷 Try...Catch...Finally 結構的關鍵字。 執行會緊接在 End Try 陳述式之後的程式碼繼續執行。 Finally 陳述式仍會繼續執行。 在 Finally 區塊中不允許使用。
Finally 選擇性。 執行離開 Try...Catch 陳述式的任何部分時,一律會執行 Finally 方塊。
finallyStatements 選擇性。 在所有其他錯誤處理都發生後所執行的陳述式。
End Try 終止 Try...Catch...Finally 結構。

備註

如果您預期在特定程式碼區段期間可能會發生特定例外狀況,請將程式碼放在 Try 區塊中,並使用 Catch 區塊來保留控制項,並在例外狀況發生時加以處理。

Try…Catch 陳述式包含 Try 區塊後面接著一個或多個 Catch 子句,指定不同例外狀況的處理常式。 在 Try 區塊中擲回例外狀況時,Visual Basic 會尋找處理例外狀況的 Catch 陳述式。 如果找不到相符的 Catch 陳述式,Visual Basic 會檢查呼叫目前方法的方法,並依此類推呼叫堆疊。 如果找不到 Catch 區塊,則 Visual Basic 會向使用者顯示未處理的例外狀況訊息,並停止執行程式。

您可以在 Try…Catch 陳述式中使用多個 Catch 陳述式。 如果要這樣做,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、TryCatch、Try Catch、Property 等],然後展開 [錯誤處理 (例外狀況)]。 如需詳細資訊,請參閱 Code Snippets

Finally 區塊

如果在結束 Try 結構之前必須運行一或多個陳述式,請使用 Finally 方塊。 控制項在離開 Try…Catch 結構前會先傳遞至 Finally 區塊。 即使在 Try 結構中的任何位置發生例外狀況也是如此。

Finally 區塊適用於執行任何必須執行的程式碼,即使發生例外狀況也是如此。 無論 Try...Catch 區塊結束的方式為何,控制項都會傳遞至 Finally 區塊。

即使您的程式碼在 TryCatch 區塊中遇到 Return 陳述式,Finally 區塊中的程式碼仍會執行。 在下列情況下,控制項不會從 TryCatch 區塊傳遞至對應的 Finally 區塊:

將執行明確傳輸至 Finally 區塊是無效的。 除非透過例外狀況,否則將執行傳輸至 Finally 區塊外是無效的。

如果 Try 陳述式不包含至少一個 Catch 區塊,則必須包含 Finally 區塊。

提示

如果您不需要 catch 特定例外狀況,Using 陳述式的行為就類似於 Try…Finally 區塊,而且不論您如何結束區塊,都保證會處置資源。 即使發生未處理的例外狀況也是如此。 如需詳細資訊,請參閱 Using 陳述式

例外狀況引數

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 運算子相關聯的工作完成時,就會在相同方法中繼續執行。 如需詳細資訊,請參閱非同步程式中的控制流程

Async 方法所傳回的工作可能會以錯誤狀態結束,代表因未處理的例外狀況而完成。 工作也可能會以取消狀態結束,導致從 await 運算式擲回 OperationCanceledException。 若要 catch 任一類型的例外狀況,請將與工作相關聯的 Await 運算式放置於 Try 區塊中,並且在 Catch 區塊中 catch 例外狀況。 本主題稍後會提供範例。

工作可能處於錯誤狀態,因為多個例外狀況導致其失敗。 例如,工作可能是對 Task.WhenAll 呼叫的結果。 當您等候這類工作時,只會攔截到其中一個例外狀況,而且無法預測會攔截到哪個例外狀況。 本主題稍後會提供範例。

Await 運算式不能位於 Catch 區塊或 Finally 區塊中。

迭代器

迭代器函式或 Get 存取子會對集合執行自訂反覆運算。 迭代器會使用 Yield 陳述式,一次一個地傳回集合中的每個項目。 您可以使用 For Each...Next 陳述式來呼叫迭代器函式。

Yield 陳述式可以位於 Try 區塊中。 包含 Yield 陳述式的 Try 區塊可以有 Catch 區塊,也可以有 Finally 區塊。 如需範例,請參閱 Try 區塊 (部分機器翻譯)。

Yield 陳述式不能位於 Catch 區塊或 Finally 區塊中。

如果 For Each 主體 (位於迭代器函式外部) 擲回例外狀況,則不會執行迭代器函式中的 Catch 區塊,但會執行迭代器函式中的 Finally 區塊。 迭代器函式中的 Catch 區塊僅攔截迭代器函式中發生的例外狀況。

部分信任情況

在部分信任情況下 (例如裝載在網路共用上的應用程式),Try...Catch...Finally 不會 catch 在叫用包含呼叫的方法之前發生的安全性例外狀況。 如果將下列範例放在伺服器共用中並在其中執行,就會產生錯誤「System.Security.SecurityException:要求錯誤」。如需安全性例外狀況的詳細資訊,請參閱 SecurityException 類別 (英文)。

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

在這種部分信任的情況下,您必須將 Process.Start 陳述式放在個別的 Sub 中。 對 Sub 的初始呼叫將會失敗。 這會讓 Try...Catch 能夠在包含 Process.StartSub 啟動且產生安全性例外狀況之前進行 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.
        Console.WriteLine("end of Try block")
    Catch ex As Exception
        ' Show the exception's message.
        Console.WriteLine(ex.Message)

        ' Show the stack trace, which is a list of methods
        ' that are currently executing.
        Console.WriteLine("Stack Trace: " & vbCrLf & ex.StackTrace)
    Finally
        ' This line executes whether or not the exception occurs.
        Console.WriteLine("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
        Console.WriteLine("NullReferenceException: " & ex.Message)
        Console.WriteLine("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

非同步方法的例外狀況處理

下列範例說明非同步方法的例外狀況處理。 若要 catch 套用至非同步工作的例外狀況,Await 運算式會位於呼叫端的 Try 區塊中,並且在 Catch 區塊中攔截例外狀況。

取消註解範例中的 Throw New Exception 行來示範例外狀況處理。 在 Catch 區塊中攔截例外狀況,將工作的 IsFaulted 屬性設定為 True,並將工作的 Exception.InnerException 屬性設定為例外狀況。

取消註解 Throw New OperationCancelledException 行來示範取消非同步處理序時會發生的情況。 在 Catch 區塊中攔截例外狀況,並將工作的 IsCanceled 屬性設定為 True。 然而,在不適用本範例的部分情況下,IsFaulted 會設定為 TrueIsCanceled 則設為 False

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 的三項工作都完成時,工作即完成。

這三個工作都會造成例外狀況。 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

另請參閱