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 语句仅在 expression 计算结果为 True 时才捕获异常。 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
块保留控制并处理异常(如果发生)。
Catch
语句包含一个后接一个或多个 Try
子句的 Try…Catch
块,这些子句为各种异常指定处理程序。 在 Try
块中引发异常时,Visual Basic 会查找处理该异常的 Catch
语句。 如果找不到匹配的 Catch
语句,则 Visual Basic 会检查调用当前方法的方法,并以此类推遍历调用堆栈。 如果未找到任何 Catch
块,则 Visual Basic 向用户显示一条未经处理的异常消息,并停止执行程序。
可以在 Try…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、For Each、TryCatch、Property 等”,然后展开“错误处理(异常)”。 有关详细信息,请参阅代码片段。
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
运算符关联的任务完成时,在同一方法中继续执行。 有关详细信息,请参阅异步程序中的控制流。
异步方法返回的任务可能以出错状态结束,从而指示由于未经处理的异常而完成。 任务也可能以已取消状态结束,这会导致从 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
的初始调用会失败。 这使得在启动包含 Process.Start
的 Sub
并产生安全异常之前,Try...Catch
到 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
处理异步方法中的多个异常
下面的示例阐释了在多个任务可能导致多个异常的情况中的异常处理。 对于 Task.WhenAll 返回的任务,Try
块具有 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