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
區塊。
type
為Exception
,例如: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
區塊。
即使您的程式碼在 Try
或 Catch
區塊中遇到 Return
陳述式,Finally
區塊中的程式碼仍會執行。 在下列情況下,控制項不會從 Try
或 Catch
區塊傳遞至對應的 Finally
區塊:
在
Try
或Catch
區塊中遇到 End 陳述式。在
Try
或Catch
區塊中擲回 StackOverflowException。
將執行明確傳輸至 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.Start
的 Sub
啟動且產生安全性例外狀況之前進行 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
會設定為 True
而 IsCanceled
則設為 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