Visual Basic .NET 中的错误处理
原文地址:http://www.microsoft.com/china/MSDN/library/archives/library/dndotnet/html/errhandlvbnet.asp
Ken Getz
MCW Technologies
2002 年 2 月
摘要:本文讨论 Visual Basic .NET 中的错误处理与 Visual Basic 6.0 中的错误处理之间的区别,主要包括 Try/Catch 块、Exception 对象、过程调用程序以及如何创建自己的 Exception 类等主题。
目标
- 将 Microsoft® Visual Basic® .NET 中的错误处理与 Visual Basic 6.0 中的错误处理进行比较
- 学习使用 Try/Catch 块处理运行时错误
- 使用 Exception 对象确定所发生的错误
- 将异常返回过程调用程序
- 创建您自己的 Exception 类
目录
结构化异常处理入门
.NET 框架使用 Visual Basic .NET 中的 Try、Catch、Finally 和 Throw 等关键字提供结构化异常处理功能。此类错误处理在 C++ 中已使用多年。随着 .NET 公共语言运行时的发布,包括 Visual Basic .NET 在内的所有 .NET 语言都可以使用这种类型的错误处理。
历史回顾
尽管自“Visual”附加至产品名称产品以来,Visual Basic 就支持它自己的错误处理机制,但是可供 Visual Basic 开发人员使用的技术似乎并不完整。在 Visual Basic 中进行错误处理时存在若干问题(参见下面的程序列表 1),这些问题让 Visual Basic 开发人员(不管是经验丰富的开发人员还是初学者)抱怨不已:
- 在 Visual Basic 6.0 中处理错误时,使用者需要在过程中不停地跳转。On Error Goto、Resume 和 Resume Next 语句都包含在代码中向前跳转或向后跳转的操作。标准的 Visual Basic 6.0 错误处理技术在一个过程内包含至少一个跳转,通常不止一个(一个向前跳转至错误处理块,一个返回至公用过程退出点)。
- 如果您遵循 Visual Basic 6.0 的良好编程经验,包括确保过程只包含一个退出点,那么放置退出点的最佳位置便是过程的中间(错误处理块之前),从而轻松忽略重要的 Exit Sub 或 Exit Function。
- 在 Visual Basic 6.0 中,不能使错误处理程序“入栈”和“出栈”。如果您希望保存当前的错误陷阱,则需要设置另外一个错误陷阱,然后返回到第一个错误陷阱。必须记住,每次更改处理程序时,都应包含正确的 On Error Goto_ 语句。
- Visual Basic 6.0 只包含一个 Err 对象。如果发生错误而您没有立即处理该错误,那么等您有机会处理该错误时,您可能已经永久性地丢失了这些错误信息。
- Visual Basic 6.0 文档几乎不涉及错误类型(即错误编号),这些错误可能是由于您在代码中执行了某个操作而收到的。唯一的办法是,通过在测试过程中触发错误来查看可以生成哪些错误编号,并在代码中捕获这些特定错误。
程序列表 1:Visual Basic 6.0 中的错误处理至少要求一个跳转,通常要求多个。
Sub TestVB6() On Error GoTo HandleErrors ' 在此处执行某个可能 ' 引发错误的操作。 ExitHere: ' 在此处执行清理代码。 ' 忽略此清理代码中 ' 的错误。 On Error Resume Next ' 执行清理代码。 Exit Sub HandleErrors: Select Case Err.Number ' 为每个要捕获的 ' 错误编号添加案例。 Case Else ' 添加“最后一个”错误处理程序。 MsgBox "错误:" & Err.Description End Select Resume ExitHere End Sub
此外,尽管 Visual Basic 开发人员过去完全能够使用 Err.Raise 方法引发错误并返回给调用过程,但这种技术一直未能成为一种标准。许多开发人员在编写供其他程序调用的代码时,只让代码返回表示成功或失败的错误值,而不会在失败时引发错误。由于可能(且容易)忽略调用的过程所返回的错误值,因此在很多情况下,在运行时因任何原因而失败的代码,都不会引发相应的错误并将错误返回给调用它的程序。
现状
随着结构化异常处理的出现,开发人员可以更容易地管理错误通知、引发错误并确定造成运行时错误的原因。与早期版本的 Visual Basic 相比,结构化异常处理提供了多种能够更加灵活地处理错误的功能:
- .NET 中的错误处理基于 Exception 类,该类不仅包含有关当前错误的信息,而且还包含一个链接的错误列表,列出那些可能触发当前错误的错误。
- 您可以继承 Exception 类来创建自己的、与基类具有相同功能的异常,也可以在必要时创建扩展功能。您所编写的代码可以捕获特定的异常,因此创建自己的 Exception 类可为您带来很大的灵活性。
- 由于 .NET 框架中的每一个类在遇到运行时错误时都会产生异常,因此开发人员将习惯于捕获异常并进行处理。这就大大增加了成功处理组件中所发生的异常的可能性。
- 您可以将 Try/Catch 块嵌入 Try、Catch 或 Finally 块中。这样开发人员就能够根据所需粒度级别来管理异常处理。
程序列表 2 列出了 Visual Basic .NET 中的一个简单异常处理程序的设计轮廓。下面的小节将详细介绍如何使用程序列表 2 中显示的各个关键字,以及如何在跟踪和引发错误时使用 Exception 类。
程序列表 2:Visual Basic .NET 中的错误处理不需要跳转。
Sub TestVBNET() Try ' 在此处执行某个可能 ' 引发错误的操作。 Catch ' 在此处处理 Try 块中 ' 发生的异常。 Finally ' 在此处执行清理代码。 End Try End Sub
提示:您可以在同一项目中混合使用旧的 Visual Basic 6.0 错误处理和 .NET 的结构化异常处理,但是不能在同一过程中混合使用。同一过程中不能同时存在 On Error 和 Try。
引发错误
以下几个示例的基本前提都相同,即:打开一个文件,检索其长度,然后关闭该文件。每个示例都使用以下代码来完成自己的任务,从示例窗体 txtFileName 上的文本框中检索文件名:
Dim lngSize As Long ' 长度为 64 位的数。 Dim s As FileStream s = File.Open(txtFileName.Text, FileMode.Open) lngSize = s.Length s.Close()
当然,由于多种原因,代码有可能会失败。例如,在出现以下情况时,代码将引发异常:
- 未找到文件。
- 路径不存在。
- 存放该文件的驱动器未就绪(也许您在不包含介质的软盘驱动器上请求某个文件的大小)。
- 没有访问文件或文件夹的权限。
- 指定的文件名无效。
还可以列出许多情形,此处不再赘述。以下几个示例讨论了此代码的一些变化形式,以演示结构化异常处理的功能。
添加错误处理
以下几节通过一系列示例,探讨有关向您已见过的示例代码添加越来越复杂的错误处理功能的问题。这些示例从尚未添加任何异常处理代码的方案开始,介绍了在 Visual Basic .NET 中捕获和识别异常的概念。
与本文对应的示例应用程序是 ErrorHandling.sln,它包括一个窗体 frmErrors,该窗体使您可以尝试此处介绍的各种方法(参见图 1)。在每种情况中,请尝试输入并不存在的文件路径或驱动器、不包含任何介质的驱动器或者可能触发文件系统错误的任何其他路径。

图 1:使用此示例窗体演示此处讨论的所有各不相同的功能。
基本情况 - 根本没有错误处理
如果您的代码中不包含任何异常处理,将会发生什么样的情况?如果是这样,运行时发生的任何错误就会回溯到 .NET 运行时;而运行时会呈现给用户一个让人误解并且可能会造成危险的对话框,如图 2 所示。为了避免出现此对话框,如果发生运行时错误,您至少需要向顶层过程添加异常处理,并在必要时在下层过程中也添加异常处理。

图 2:包含的 Continue(继续)按钮使 .NET 默认错误处理程序变得有些危险。此外,其中的详细信息并不是您希望用户看到的内容。
提示:与在 Visual Basic 6.0 中一样,如果您没有在过程中添加异常处理,而该过程中发生了错误,那么 .NET 运行时会将当前过程从调用堆栈中弹出,并返回到前一过程。如果该过程包含错误处理,则运行时将使用该代码。否则,运行时将继续从堆栈中弹出过程,直至退回到一个确实包含错误处理的过程。如果没有任何过程包含错误处理,则在返回到第一个调用过程的过程中,.NET 运行时将自行处理错误,如图 2 所示。
添加简单的 Try/Catch/End Try 块
为了恰当地处理运行时错误,请在要保护的任何代码附近添加一个 Try/Catch/End Try 块。如果在 Try 块的代码中发生运行时错误,将立即使用 Catch 块中的代码继续执行:
Try
s = File.Open(txtFileName.Text, FileMode.Open)
lngSize = s.Length
s.Close()
Catch
MessageBox.Show("Error occurred!")
End Try
此代码运行时,应用程序不会显示警报并且终止运行,您将看到一条简单的“Error occurred”警报,而应用程序可以继续运行。要亲自测试这种情况,请在示例窗体的 Error Handling(错误处理)组合框中选择 Simple Catch(简单捕捉)选项。
提示:如果您在过程中添加了 Try/Catch/End Try 块,则需要至少包含一个 Catch 块(有关包含多个 Catch 块的详细信息,请参阅下文)。如果要忽略所发生的错误,只需在 Catch 块中不包含任何内容。这并不是一个很好的方法,但却可以悄然无声地忽略所发生的任何错误。
确定所发生的事件
发生运行时错误时,如何确定错误类型呢,又如何处理该错误呢?您可以创建一个声明使用 As Exception 的变量来检索错误信息。Exception 类能提供有关运行时错误的信息,如表 1 所示。
| 成员 | 说明 |
|---|---|
| HelpLink | 链接到与此异常相关联的帮助文件。 |
| InnerException | 对内部异常的引用,如果此异常基于前一个异常,则内部异常指最初发生的异常。异常可以嵌套,也就是说,当某个过程发生异常时,它可以将另一个异常嵌套到自己所引发的异常中,并将两个异常都传递给调用程序。InnerException 属性提供对内部异常的访问。 |
| Message | 错误消息文本。 |
| StackTrace | 出错点堆栈跟踪(作为单个字符串)。 |
| TargetSite | 引发异常的方法的名称。 |
| ToString | 将异常名称、说明和当前堆栈转储转化为单个字符串。 |
| Message | 返回所发生错误的说明。 |
表 1:Exception 类的有用成员
Catch 块包含对变量的引用,如下所示:
Try ' 可触发异常的代码。 Catch e As Exception ' 此处使用 e 来处理异常。 End Try
您还可以在 Catch 块之外声明 Exception 变量:
Dim e As Exception Try ' 可触发异常的代码。 Catch e ' 此处使用 e 来处理异常。 End Try
您可以使用类似以下的代码来捕获异常,并显示表示已出现问题的文本:
' 示例窗体上的 Simple Exception(简单异常)选项 Private Sub SimpleException() Dim lngSize As Long Dim s As FileStream ' Display the entire contents of the Exception object. Try s = File.Open(txtFileName.Text, FileMode.Open) lngSize = s.Length s.Close() Catch e As Exception MessageBox.Show(e.ToString) End Try End Sub
提示:Exception 对象的名称并不重要。示例代码使用 e 作为变量名称,不过您可以任意选择。如果您觉得该名称不方便在您自己的过程中使用,您可以选择其他名称。
如果只希望显示表示捕获的特定错误的错误消息,则可以使用 Exception 类的 Message 属性,如下所示:
' 示例窗体中的 Which Exception 选项。
Private Sub WhichException()
Dim lngSize As Long
Dim s As FileStream
' 现在您至少可以知道发生了什么问题!
Try
s = File.Open(txtFileName.Text, FileMode.Open)
lngSize = s.Length
s.Close()
Catch e As Exception
MessageBox.Show("Error occurred: " & e.Message)
End Try
End Sub
到目前为止,您已经了解了如何在发生异常时捕获异常,以及如何向用户指示所发生的问题。通常,您还需要能够根据所发生的特定错误采取相应的措施。在 Visual Basic 6.0 中,可以根据当前的错误编号添加 Select Case 块。在 Visual Basic .NET 中,则涉及到为每个希望单独捕获的错误添加其他 Catch 块。下一节将深入探讨如何在您的过程中添加此功能。
处理特定异常
.NET 框架提供了大量的特定异常类,所有这些异常类都是从基本的 Exception 类继承而来的。在 .NET 框架文档中,您会看到一些表,它们列出了在调用任何方法时都可能出现的所有异常。例如,从 .NET 框架文档拍摄到的图 3 使您可以轻松地确定调用 File.Open 方法时可能发生的问题。

图 3:.NET 文档列出了调用 File.Open 方法时可能发生的所有异常
您的过程可以包含足够多的 Catch 块,以便您分别处理各个异常。来自示例项目的以下过程将测试若干个不同的异常,并逐个处理每个异常。测试此过程时,需要尝试许多特定的异常情况。例如,将文件名更改为:
- 在有效的路径中,但选择的文件并不存在。
- 位于不存在的驱动器上。
- 位于不存在的路径中。
- 位于尚未就绪的驱动器上。
'示例窗体上的 Multiple Exceptions(多个异常)选项。
Private Sub MultipleExceptions()
Dim lngSize As Long
Dim s As FileStream
Try
s = File.Open(txtFileName.Text, FileMode.Open)
lngSize = s.Length
s.Close()
Catch e As ArgumentException
MessageBox.Show( _
"You specified an invalid filename。 " & _
"Make sure you enter something besides spaces.")
Catch e As FileNotFoundException
MessageBox.Show( _
"The file you specified can't be found。 " & _
"Please try again.")
Catch e As ArgumentNullException
MessageBox.Show("You passed in a Null argument.")
Catch e As UnauthorizedAccessException
MessageBox.Show( _
"You specified a folder name, not a file name.")
Catch e As DirectoryNotFoundException
MessageBox.Show( _
"You specified a folder that doesn't exist " & _
"or can't be found.")
Catch e As SecurityException
MessageBox.Show( _
"You don't have sufficient rights " & _
"to open the selected file.")
Catch e As IOException
' 一般异常处理程序,用于任何
' 尚未捕获的 IO 错误。 此处,应为
' 驱动器未就绪。
MessageBox.Show( _
"The drive you selected is not ready。 " & _
"Make sure the drive contains valid media.")
Catch e As Exception
MessageBox.Show("An unknown error occurred.")
End Try
End Sub
确定异常的层次结构
通过图 3 所示的 Exceptions 表中的链接可以访问相关 Exception 对象的文档。此文档包含继承层次结构,如图 4 所示。在添加多个 Catch 块时,您需要了解对象的这种层次结构。

图 4:继承层次结构使您可以确定对象的“is a”(是一个)关系。
使用异常继承层次结构
在图 4 所示的异常层次结构中,您可以看到 ArgumentNullException 从 ArgumentException 继承而来,ArgumentException 从 SystemException 继承而来,而 SystemException 则是从 Exception 继承而来的。层次结构中的每一级都表示一个更加具体的级别,即:层次越低,异常就越具体。
由于每一级都是从其上一级的类继承而来,因此每个较低的级别都是其上一级所指定类型的一个实例。ArgumentNullException“is a(n)”(是一个)ArgumentException,ArgumentException “is a”(是一个)SystemException,而 SystemException 则“is a(n)”(是一个)Exception。此处的“is a”(是一个)用引号引起来,因为它是一个有意义的操作符,当具有多个 Catch 块时,这些块通过“is a”(是一个)规则与当前的异常进行匹配。也就是说,在处理多个 Catch 块时,如果运行时首先发现某个匹配中的当前异常符合 Catch 所捕获的异常的“is a”(是一个)规则,运行时将使用该 Catch 块来处理异常,而不再继续进行查找。换句话说,Catch 块的顺序非常重要,它是基于这种“is a”(是一个)关系的。所有异常都是从基本的 Exception 类继承而来的,因此您始终需要包含一个 Catch 块来最后处理基本的 Exception 类(如果有)。
引发错误
您可能需要从过程中引发错误,以指示调用程序发生了某种异常。您可能只希望传递回由 .NET 框架提供的标准运行时异常,或者希望创建自己的异常条件。在两种情况中,您都将使用 Throw 关键字以从当前块中引发异常。
注意:Throw 关键字的工作方式与 Visual Basic 6.0 中的 Err.Raise 方法十分相似。
使用 Throw 关键字
您可以通过两种方式使用 Throw 关键字。您可以:
- 将刚刚发生的错误从 Catch 块中抛回到调用程序:
Catch e As Exception Throw - 从包括 Try 块的任何代码中抛出错误:
Throw New FileNotFoundException()注意:第一种方法(抛出刚刚发生的异常)仅适用于 Catch 块。第二种方法(抛出新错误)则适用于任何情况。
搜索处理程序
抛出异常时,.NET 运行时将从下至上搜索过程调用堆栈,以查找相应的异常处理程序。(如果抛出异常时您正处于某个 Try 块中,运行时将首先使用本地的 Catch 块 [如果有] 来处理异常。)一旦运行时为抛出的异常找到一个 Catch 块,它将执行在该位置找到的代码。如果在调用堆栈中没能找到适当的 Catch 块,运行时将自行处理异常(如上面的图 2 所示)。
错误处理选项
您可以确定处理哪些异常,而将哪些异常返回调用程序。发生异常时,您可以选择:
- 不进行任何操作。在这种情况下,.NET 运行时将自动将异常返回到调用代码的过程。
- 捕捉特定错误。在这种情况下,将不会返回您已处理的异常,但是那些不由您处理的异常将被抛回到调用过程。
- 处理所有错误。在 Catch 块中添加一个“Catch e as Exception”块,这样异常处理将处理所有的错误,除非您自己明确抛出某个错误。
- 抛出错误。当然,您可以选择将所有错误抛回调用程序。使用 Throw 语句,您可以将当前错误或任何其他错误发送到调用程序的异常处理程序。
提示:如果使用 Throw 关键字抛出异常,Visual Basic 6.0 风格的 On Error Goto 错误处理也可以捕获该错误。也就是说,无论您采用旧的错误处理规则还是新的错误处理规则,.NET 运行时都将对所有异常采用相同的处理方式。
传递错误信息
如果要截取不同的异常并将它们作为单个异常类型全部返回到调用程序,使用 Throw 可以非常轻松地完成此操作。在下一个示例中,代码将捕获所有异常,而且无论导致异常的原因是什么,代码都会将一个 FileNotFoundException 对象抛回给调用程序。在某些情况下(如本例),调用过程可能并不关心实际发生的事情,也不关心为什么无法找到文件。调用程序可能只关心该文件不可用,并且需要从其他不同的异常中辨别该特定异常。
Exception 对象构造函数
Exception 对象的构造函数可以通过几种方式进行重载。您可以不传入任何参数(您将获得一个普通的 Exception 对象,包含其属性的默认值);可以传入字符串,该字符串指示要传回调用程序的错误消息;或者传入一个字符串和 Exception 对象,指示错误消息和所发生的原始异常(填写传回到调用程序的异常的 InnerException 属性)。此处的示例使用最终构造函数传回内部异常。
除了代码所引发的异常外,您可能还希望调用程序能够使用原来的异常信息。在这种情况下,您会发现 Exception 类的构造函数所提供的重载版本使您可以指定内部异常。也就是说,您可以传递最初引发错误的异常对象。调用程序可在必要时检查此异常。
提示:异常的 InnerException 属性本身是一个 Exception 对象,它还可能包含非 Nothing 的 InnerException 属性。因此,当您开始深入研究 InnerException 属性时,您可以依次处理异常链接表中的异常,直至链接表尾。为了弄清可能已发生的所有错误,您需要反复检索 InnerException 属性,直到属性返回 Nothing 为止。
在下面的示例中,无论接收到什么错误,TestThrow 过程都会向其调用程序抛回一个 FileNotFoundException。此外,它将使用原始异常对象填写异常的 InnerException 属性。本例将显示虚构的错误消息以及与原始异常相关联的文本:
' 示例窗体上的 Throw Exception(抛出异常)选项。
Private Sub ThrowException()
Dim lngSize As Long
Dim s As FileStream
' 捕捉由调用的过程抛出的异常。
Try
TestThrow()
Catch e As FileNotFoundException
MessageBox.Show("Error occurred: " & e.Message)
' 使用 e.InnerException 获取
' 触发此异常的错误。
MessageBox.Show(e.InnerException.Message)
End Try
End Sub
Private Sub TestThrow()
Dim lngSize As Long
Dim s As FileStream
' 无论发生什么情况,都抛回
' 一个“未找到文件”异常。
Try
s = File.Open(txtFileName.Text, FileMode.Open)
lngSize = s.Length
s.Close()
Catch e As Exception
Throw (New FileNotFoundException( _
"Unable to open the specified file.", e))
End Try
End Sub
无条件运行代码
您可能会发现,除了 Try 和 Catch 块中的代码外,您还需要添加无论是否发生错误都会运行的代码。您可能需要释放资源、关闭文件或处理在任何情况下都会发生的其他问题。要无条件运行代码,您需要使用 Finally 块。
Finally 块
要无条件运行代码,请在所有 Catch 块后添加 Finally 块。即使您的代码抛出异常,并且您在 Catch 块中添加了显式 Exit Function(或 Exit Sub)语句,此块中的代码也会运行。Finally 块中的代码将在异常处理代码之后、控制返回到调用过程之前运行。
例如,无论处理文件时是否会发生错误,您都可以确定您的代码需要将 FileStream 对象变量设置为 Nothing。您可以对过程进行如下修改,以便在无论是否发生错误的情况下都可以调用结束代码:
' 示例窗体中的 Test Finally (最终测试)选项。
Private Sub TestFinally()
Dim lngSize As Long
Dim s As FileStream
Try
s = File.Open(txtFileName.Text, FileMode.Open)
lngSize = s.Length
s.Close()
Catch e As Exception
MessageBox.Show(e.Message)
Finally
' 无论发生什么情况都运行此代码。
s = Nothing
End Try
End Sub
提示:尽管您的 Try/End Try 块必须包含一个或多个 Catch 块,或者包含一个 Finally 块,但是并不需要同时包含这两种块。也就是说,没有 Catch 块,Finally 块也同样有效。为什么没有包含 Catch 块,就需要包含一个 Finally 块呢?这是因为:如果过程中发生异常,.NET 运行时将查找相应的异常处理程序,这可能意味着 .NET 运行时将离开您的过程(如果没有 Catch 块,则必然会发生),在调用堆栈中寻找相应的异常处理程序。如果您希望在运行时离开您的过程之前运行代码,就必须包含一个 Finally 块。例如,如果您正在处理的对象提供了 Dispose 方法,而您希望确保在离开过程之前调用该方法,那么无论您是否使用 Catch 块,都需要将对 Dispose 方法的调用放置在 Finally 块中。这样,即使发生错误,您也可以在 .NET 框架将异常发送回调用过程之前调用 Dispose 方法。
创建 Exception 类
您也许会发现,.NET 框架并不提供能满足特定需求的 Exception 类。例如,如果用户选择的文件大于 100 字节,您可能需要引发异常。尽管通常情况下不会将这种情况视为异常,但在您的应用程序却可能是一个错误情况。
要创建自己的异常类,请执行以下步骤:
- 创建一个新类。
- 从 ApplicationException 基类继承。
注意:实际上,您可以从本身是从 Exception 类继承而来的任何类继承。例如,您可以从 IOException 类或 FileNotFoundException 类继承。任何一个都可以用作您自己的异常的基类。但是,本文建议您不要直接从 Exception 继承。
- 提供您自己的 New 方法(必要时添加适当的重载)。回调 MyBase.New,以包括对基类构造函数的调用。
- 添加所需的任何其他功能。
FileTooLargeException 类
示例项目包含以下类定义(位于 frmErrors.vb 模块中),提供了 FileTooLargeException 的定义:
Public Class FileTooLargeException
Inherits ApplicationException
Private mlngFileSize As Long
Public Sub New(ByVal Message As String)
MyBase.New(Message)
End Sub
Public Sub New(ByVal Message As String, _
ByVal Inner As Exception)
MyBase.New(Message, Inner)
End Sub
Public Sub New(ByVal Message As String, _
ByVal Inner As Exception, ByVal FileSize As Long)
MyBase.New(Message, Inner)
mlngFileSize = FileSize
End Sub
Public ReadOnly Property FileSize() As Long
Get
Return mlngFileSize
End Get
End Property
End Class
这个类提供了标准的 Exception 类属性(因为它是从 ApplicationException 继承而来的),但是添加了一个新方法,即添加了一个新的构造函数,使您能够传入触发异常的文件的大小。此外,它还提供了一个 FileSize 属性,以便过程的调用程序能够确定触发异常的文件的大小。
此处所示的 GetSize 函数将尝试打开一个文件。如果请求的文件太大,GetSize 将向其调用程序抛回一个 FileTooLargeException,并传递其本身的错误消息和请求的文件的大小:
Private Function GetSize( _
ByVal strFileName As String) As Long
Dim lngSize As Long
Dim s As FileStream
' 返回文件大小。 如果文件大于 100 字节
'(任意大小),将向调用程序抛出一个
'(用户定义的异常)。
Try
s = File.Open(txtFileName.Text, FileMode.Open)
lngSize = s.Length
s.Close()
If lngSize > 100 Then
' 传回新的异常。没有要传回的
' 内部异常,因此传递 Nothing。
Throw (New FileTooLargeException( _
"The file you selected is too large.", _
Nothing, lngSize))
End If
Return lngSize
Catch
' 将异常直接抛回给调用程序。
Throw
Finally
' 无论发生什么情况都运行此代码。
s = Nothing
End Try
End Function
测试过程将传入您在示例窗体中指定的文件,并捕获 FileTooLargeException。在该特定 Catch 块中,代码将检索异常的 FileSize 属性,并且由于此特定异常 FileTooLargeException 确实提供该属性,因此代码的编译和运行都正常(即便普通的 Exception 对象不提供 FileSize 属性,也会如此):
' 示例窗体中的 User-Defined Exception(用户定义的异常)选项。
Private Sub UserDefinedException()
Dim lngSize As Long
' 测试用户定义的异常。
Try
lngSize = GetSize(txtFileName.Text)
Catch e As FileTooLargeException
MessageBox.Show( _
String.Format( _
"Please select a smaller file! " & _
"The file you selected was {0} bytes.", _
e.FileSize))
Catch e As Exception
MessageBox.Show(e.Message)
End Try
End Sub
提示:您会发现能够创建您自己的异常类这一点非常重要,这样,无论何时需要在标准的错误信息中添加自己的信息时,都可以从基本的 ApplicationException 类继承。您可能需要创建一个可提供完整堆栈框架信息的异常类(即包含调用堆栈的某种数据结构),而不是 .NET 框架在其 StackFrame 属性中为您提供的简单字符串。您可以使用 StackTrace 类及其成员实现这一目的。有关 StackTrace 类和 StackFrame 类的详细信息,请参阅 .NET 框架文档。
总结
- 结构化异常处理的功能比 Visual Basic 6.0 所提供的错误处理功能更强大。
- 使用一个 Try 块可以向代码段添加异常处理。
- 在必要时添加 Catch 块,以捕获各种异常。
- .NET 运行时可以依次处理 Catch 块,以查找当前异常的“is a”(是一个)匹配。它将使用找到的第一个匹配块。
- 您可以嵌套 Try 块,以便轻松而有效地推入和弹出异常处理状态。
- 在 Try 块中添加一个 Finally 块,这样无论是否发生错误,都可以无条件运行代码。
- 您可以创建从基本 Exception 类(或任何从该类继承的类)继承而来的异常类,以添加自己所需的功能。
关于作者
Ken Getz 是 MCW Technologies 的资深顾问,他的工作涉及编程、著书和培训。他精通用 Microsoft Access、Visual Basic 以及 Office 和 BackOffice 套件编写的工具和应用程序。Ken 还与其他人一起编写了许多书籍,包括:与 Paul Litwin 和 Mike Gilbert 合编的《Access 97 Developer's Handbook》;与 Paul Litwin 和 Mike Gilbert 合编的《Access 2000 Developer's Handbooks》;与 Paul Litwin 和 Mike Gunderloy 合编的《Access 2002 Developer's Handbooks》;与 Mike Gilbert 合编的《Visual Basic Language Language Developer's Handbook》;以及与 Mike Gilbert (Sybex) 合编的《VBA Developer's Handbook》。他还参与了 AppDev 培训资料的编写工作,并从事这方面的教学工作。Ken 经常在技术会议上发言,自 1994 年以来,每一届的 Microsoft Tech*Ed 会议上他都会发表演讲。Ken 是 Access/VB/SQL Advisor 杂志的技术编辑和 Informant Communication Group 属下的《Microsoft Office Solutions》杂志的特约编辑。
关于 Informant Communications Group
Informant Communications Group, Inc. (www.informant.com) 是一家专注于信息技术行业的多媒体公司。ICG 成立于 1990 年,致力于与软件开发有关的出版物、会议、目录发布和 Web 站点等领域。ICG 在美国和英国均设有办事处,目前已成为享有盛誉的媒体和营销内容集成商,并以高质量的技术信息满足 IT 人员不断增长的需求。
© 2002 Informant Communications Group 和 Microsoft Corporation 版权所有。
浙公网安备 33010602011771号