博客园  :: 首页  :: 新随笔  :: 联系 :: 管理

Visual Basic .NET中的异常处理简介

Posted on 2008-03-17 10:22  codingsilence  阅读(428)  评论(0编辑  收藏  举报

Visual Basic .NET中的异常处理简介
摘要:本文简单介绍了 visual basic .net 中的结构化异常处理和非结构化异常处理。文中包括了各种注意事项,可帮助您选择正确的异常处理选项、各选项涉及的步骤、如何创建自己的异常连同异常对象的属性。最后在表中列出了预定义的异常类及其派生类。
  简介

  只有极其优秀的程式员才能一步到位地编写出完美的代码,其他程式员必须经过不断的修改才能研发出成功的应用程式。
  幸运的是,microsoft? visual basic? .net 提供了两种处理异常的方法。第一种是非结构化异常处理,他符合 visual basic 早期版本中的异常处理规则。第二种是结构化异常处理,类似于 microsoft? visual c#? 或 microsoft? visual c++? 中的异常处理方式。
  本文简单介绍了结构化异常处理和非结构化异常处理,面向的读者是 visual basic 初级研发人员,或从 visual basic 早期版本转换到 visual basic .net 的研发人员。文中包括了各种注意事项,可帮助您选择正确的异常处理选项、各选项涉及的步骤、如何创建自己的异常连同异常对象的属性。阅读本文后,您将了解如何连同何时在代码中加入异常处理。  “错误”和“异常”的定义
  术语“错误”和“异常”经常被混用。实际上,错误是指在执行代码过程中发生的事件,他中断或干扰代码的正常流程并创建异常对象。当错误中断流程时,该程式将尝试寻找异常处理程式(一段告诉程式如何对错误做出响应的代码),以帮助程式恢复流程。换句话说,错误是个事件,而异常是该事件创建的对象。
  程式员使用短语“产生异常”时,表示存在问题的方法发生错误,并创建异常对象(包含该错误的信息及发生的时间和位置)来响应该错误。导致出现错误和随后异常的因素包括用户错误、资源失败和编程逻辑失败。这些错误和代码实现特定任务的方法有关,而和该任务的目的无关。
  针对本文的目的,“异常处理”的意思是解释和响应因错误而产生的异常。  结构化异常处理和非结构化异常处理 - 分别在什么情况下使用
  简单来说,结构化异常处理就是使用包含异常、单独的代码块和筛选器的控制结构创建异常处理机制。他使代码能够区分不同的错误类别,并根据相应的情况做出响应。在非结构化异常处理中,代码开始处的 on error 语句将处理任何的异常。
  和非结构化异常处理相比,结构化异常处理更强大,更具普遍性和灵活性。如有可能,请尽量使用结构化异常处理。然而,在以下情况下可能需要使用非结构化异常处理:
升级使用 visual basic 早期版本编写的应用程式时。
研发应用程式的初期版本或草稿版本,而且,即使程式不能正常关闭您也不会介意。
事先已确切知道将导致出现异常的原因。
迫于截止日期而需要走捷径。
代码很琐碎或太短,只需要测试其中产生异常的代码部分。
需要使用 resume next 语句,而结构化异常处理不支持该语句。

  不能在同一个函数中将结构化异常处理和非结构化异常处理组合使用。假如使用了 on error 语句,则不能在同一个函数中使用 try...catch 语句。
  不管在代码中使用哪种异常处理,都必须回退一步先检查代码假设的条件。例如,当应用程式需要用户输入电话号码时,以下假设开始起作用:
用户必须输入数字而不是字符。
输入的号码必须符合某种格式。
用户不能输入空字符串。
用户只能输入一个电话号码。

  用户的输入可能会不符合上述一条或全部假设。完善的代码需要足够的异常处理,以允许在违反上述假设的情况下恢复应用程式。
  除非能够确保您的方法在任何情况下都不会产生异常,否则请考虑使用信息性异常处理。异常处理应该直观。除了指出产生错误以外,异常处理产生的消息中还应指示错误产生的原因及其位置。诸如“发生错误”之类的毫无意义的消息只能使用户感到失望。
  

  结构化异常处理
  结构化异常处理测试特定的代码片段,并在发生异常时改编此异常处理代码,以适应导致该异常的环境。和非结构化异常处理相比,结构化异常处理在大程式中运行得较快,在响应错误时更具灵活性,且具备更高的应用程式可靠性。
  try...catch...finally 控制结构是结构化异常处理的基本结构。他测试代码片段,筛选该代码执行过程中产生的异常,并根据产生的异常类型做出不同的响应。
  try...catch...finally 块
  try...catch...finally 控制结构测试代码片段,并指导应用程式如何处理各种不同类型的错误。在此过程中,该结构的三个组成部分分别扮演着特定的角色。
try 语句提供正在测试异常的代码。
catch 子句标识和特定异常相关联的代码块。catch when 块指导代码在特定情况下执行。不包含 when 的 catch 子句将响应任何异常。因此,代码中可能包含一系列特定的 catch...when 语句,每条语句响应特定类型的异常,最后使用通用的 catch 块来响应前面的 catch...when 子句未能截取的任何异常。
不管 try 块中是否发生异常,finally 语句所包含的代码始终会执行。即使在 exit try 或 exit sub 语句后也会执行 finally 语句。此代码通常执行清除任务,例如关闭文档或清除缓冲区。

  catch 子句的用途
  catch 子句可采用三种格式:catch、catch...as 和 catch...when。
  不包含 when 关键字的 catch 子句允许相关的语句块处理任何异常。catch...as 和 catch...when 子句捕获特定的异常,并允许相关的语句块指导应用程式如何处理。也能够将 catch...as 和 catch...when 子句组合成一条语句,例如:catch ex as exception when intresult <> 0。
  假如异常是由资源失败引起的,该子句应该标识此资源,并在可能的情况下提供解决问题的建议或避免出现此问题的提示。假如异常是由编程逻辑失败引起的,该子句应该允许应用程式尽可能安全地退出。假如异常是由用户错误引起的,该子句应该允许用户更正错误并继续进行操作。
  catch 子句按其在代码中出现的顺序执行。因此,当在整个代码序列中执行时,catch 子句应该从特定部分移到通用部分。例如,在检查完类型后再检查其基本类型。处理 system.exception 的 catch 块应该放置到最后,在处理完任何其他可能的异常之后再执行。
imports system
try
  varavailableseats = varauditoriumseats - varnumberofguests
  catch ex as exception when varauditoriumseats = 0
  msgbox("观众席没有座位!")
 exit sub
  catch ex as exception when varavailableseats < 0
  msgbox("没有空余的座位。")
  exit sub
  finally msgbox("谢谢您对我们的音乐会感兴趣。")
end try  

  exception 对象
  exception 对象提供所发生异常的有关信息。每次发生异常时,都将配置 err 对象的属性,并创建一个新的 exception 对象实例。查看其属性能够确定代码位置、类型连同异常的起因。
  以下是 exception 对象的一些常用属性:
helplink 属性包含一个 url,指导用户进一步查询该异常的有关信息。
hresult 属性获取或配置分配给异常的数值 hresult。hresult 是个 32 位数值,包含三个字段:严重性代码、设备代码和错误代码。严重性代码指示返回的值表示的是信息、警告还是错误。设备代码标识负责异常的系统区域。错误代码是分配给错误的唯一编号。
innerexception 属性返回一个异常对象,代表发生异常时正在处理的异常。处理外部异常的代码也许能够使用内部异常的信息,从而更准确地处理外部表达式。
message 属性包含一个字符串,他是一些文本消息,告知用户错误的性质连同处理该错误的最好方法。创建异常对象时,用户能够提供最适用于特定异常的字符串。假如用户没有提供,将使用默认字符串并根据当前情况进行格式化。
source 属性获取或配置一个字符串,该字符串包含产生异常的对象的名称或发生异常的程式集的名称。
stacktrace 属性包含堆栈跟踪,可用于确定代码中发生错误的位置。stacktrace 列出发生异常前已调用的任何方法,连同在源代码中发生调用的行号。
targetsite 属性获取产生当前异常的方法的名称。假如无法获取名称并且堆栈跟踪不是 nothing,则 targetsite 属性将从堆栈跟踪获取此方法的名称。

  创建自己的异常用于结构化异常处理
  在 exception 基类中有两种已定义的异常子类:system.exception 和 application.exception。
  system.exception 是 .net 框架用于从中派生预定义公共语言运行时异常类的类。当发生非致命错误时,公共语言运行时将产生该异常。system.exception 不提供有关异常原因的信息。
  注意:有关预定义公共语言运行时异常类的周详信息,请参阅本文末尾的表1,该表列出了预定义的异常类及其起因和派生类。
  可通过从 application.exception 类中继承,来创建自己的应用程式异常类。请养成良好的编程习惯,使异常类的名称以单词“exception”结尾,例如 outofmoneyexception 或 toomuchrainexception。
  以下示例定义了一个异常类,并为其定义了三个构造函数,每个函数使用了不同的参数。
imports system
public class gardenexception
 inherits system.applicationexception
 public sub new()
 end sub
  为异常创建 sub new,允许在发生异常时
  配置消息属性。
 public sub new(message as string)
   mybase.new(message)
  end sub
  创建 sub new,用于在需要包含内部
  异常时使用。
 public sub new(message as string, inner as exception)
   mybase.new(message)
 end sub
end class

注意:将远程功能和用户定义的异常组合使用时,必须确保在远程执行代码时能够获取用户定义异常的元数据,包括在应用程式域之间发生的异常。

  结构化异常处理示例
  此代码示例是个简单的 try...catch 块,他先检查 arithmeticexception,然后检查普通异常。
imports system
  sub main()
   dim x as integer = 0
   try
     dim y as integer = 100 / x
   catch ex as arithmeticexception
     messagebox.show(ex.message)
   catch ex as exception
     msgbox(ex.message)
   end try
  end sub main

  此代码示例是个和某应用程式相关的 try...catch...finally 块,该应用程式打开一个文档并进行检查。注意,即使 exit sub 在代码中位于 finally 前面,finally 语句也将执行。
imports system
  sub openmyfile
   dim thisfile as object
   try
     fileopen(1, thisfile, openmode.input)
   catch ex as exception
     msgbox (ex.message)
     exit sub
   finally
     fileclose(1)
   end try
  end sub


Visual Basic .NET中的异常处理简介下
作者:Microsoft  
  非结构化异常处理
  非结构化异常处理通过 Err 对象和以下三种语句来实现:On Error、Resume 和 Error。On Error 语句创建单个异常处理程序以捕捉发生的所有异常,您可以在以后改变处理程序的位置,但一次只能有一个处理程序。此方法可以跟踪最近产生的异常和最近的异常处理程序的位置。在方法开始时,异常和异常处理程序的位置都设置为 Nothing。
  要在代码中生成运行时错误,请使用 Raise 方法。每次在错误处理例程中发生 Exit Sub、Exit Function、Exit Property、Resume 或 Resume Next 语句时,Err 对象的属性都将重置为零或零长度字符串。在错误处理例程外部使用上述任何语句都不会重置其属性。如果确实需要重置属性,可以使用 Clear 方法重置 Err 对象。  Error 对象
  Err 对象属性的值由刚刚发生的错误决定。下表列出了该对象的属性及其简单说明。 属性 说明
Description 对错误进行简单说明的文本消息。
Helpcontext 整数,包含帮助文件中某个主题的上下文标识符。
Helpfile 字符串表达式,包含帮助文件的完全限定路径。
LastDLL 由于调用动态链接库 (DLL) 而产生的系统错误代码。此 DLL 是发生错误之前最后调用的 DLL。
Number 指定错误的数值。
Source 字符串表达式,代表产生错误的对象或应用程序。

  下面的示例显示了如何在非结构化错误处理中使用上述某些属性: On Error Resume Next
Err.Clear
Err.Raise(33333)
Err.Description = "您没有输入数字!"
MsgBox(Err.Number)
MsgBox(Err.Description)
Msg = "请按 F1 或“帮助”查看 " & Err.HelpFile & " 中有关以下帮助内容的" & _
"主题:" & Err.HelpContext
MsgBox(Msg)

  On Error GoTo 语句
  On Error GoTo 语句启用异常处理的某个例程,并指定该例程在此过程中的位置。它使用标签或行号,指出特定异常处理例程在代码中的位置。使用 -1 时,在过程内部禁用错误处理。使用 0 时,禁用当前异常。如果没有 On Error 语句,并且在当前调用堆栈中所有方法均未处理异常,则发生任何运行时错误都将是致命的:执行过程停止并显示错误消息。
  下表列出了 On Error GoTo 语句可能使用的方法。 语句 任务
On Error Goto -1 将 Err 对象重置为 Nothing,从而在例程中禁用错误处理
On Error Goto 0 将最后的异常处理程序位置重置为 Nothing,从而禁用异常
On Error Goto <标签名> 将指定标签设置为异常处理程序的位置


On Error Resume Next 创建 Resume Next 行为,作为最近的异常处理程序的位置

  Resume 和 Resume Next
  Resume 语句本身可以将控制权返回导致异常的语句。执行过程将返回到最初产生异常的那一行。
  相比较而言,Resume Next 语句将在发生异常后恢复执行过程。该语句指定,在异常事件中,控制权将传递给紧接发生异常语句之后的语句。Resume Next 的使用可允许出现不太严重的失败。引发错误的语句失败,但应用程序将继续执行,且允许用户改正错误并继续进行操作。与此类似,Resume <标签> 将控制权传递给在其 line 参数中指定的标签。确保行标签与调用它的代码位于相同的过程中,因为它不能跨函数使用。
  Resume 在错误处理例程中必须单独使用。它在这种例程的外部引发错误。  Error 语句
  Visual Basic .NET 支持 Error 语句仅仅是为了保持向后兼容。在新代码中,使用 Err 对象的 Raise 方法生成运行时错误。  非结构化异常处理示例
  以下示例是非结构化错误处理的一种基本方法。当 FlawlessCode 遇到错误时,执行过程将转移到 Whoops,它为用户提供该错误的有关信息(主要包含在 Err 对象的 Description 属性中的信息): Private Sub FlawlessCode()
On Error Goto Whoops
  ' 代码要做很多事情,不要过多
  ' 地研究错误处理代码。
  Return
Whoops:
' 为用户提供错误信息。
  MsgBox ("意外错误:" & Err.Description)
  Return
End Sub

  以下示例显示了如何使用 Err 对象构造错误消息对话框。 Dim ErrorMessage as String
' 如果发生错误则构造错误消息。
On Error Resume Next
Err.Raise (13) ' 生成“类型不匹配”错误。
' 查看是否出现错误。如果是,则显示消息。
If Err.Number <> 0 Then
  ErrorMessage = "错误 #" & Str(Err.Number) & " 原因是" _
   & Err.Source & vbCrLf & Err.Description
' 将该消息显示为关键消息。
  MsgBox(ErrorMessage, MsgBoxStyle.Critical, "错误")
End If
  总结
  到现在为止,您应该清楚地了解了结构化异常处理和非结构化异常处理之间的区别,以及 Visual Basic .NET 中结构化异常处理功能的优势。通常情况下,结构化异常处理即可满足您的需求,但在少数情况下仍然可能需要使用非结构化异常处理。
  在确保异常得到处理的同时,不要过多地列举它们,否则会导致性能下降。Try 结构是很有条理的,易于编写且易于阅读,它能生成有效的代码。编写处理一个或多个可能异常的代码时,都应该使用该结构。此方法极其有效,您甚至愿意在正常情况下使用异常来控制逻辑流程。例如,替代 If 或 Select 语句。处理异常是很有效的,但应该到真正发生异常时使用。
  下表列出了预定义的异常类及其起因和派生类。
表 1 异常类 产生原因 派生类
AppDomainUnloadedException 尝试访问未加载的应用程序域 无
ArgumentException 为方法提供的一个或多个参数无效 ArgumentNullException
ArgumentOutOfRangeException
ComponentModel.InvalidEnum
ArgumentException
DuplicateWaitObjectException
ArithmeticException 在算法、强制类型转换或转换操作上发生错误 DivideByZeroException
NotFiniteNumberException
OverflowException
ArrayTypeMismatchException 尝试在数组中存储错误类型的元素 无

BadImageFormatException DLL 或可执行程序的文件映像无效 无
CannotUnloadAppDomainException 尝试卸载应用程序域失败 无
ComponentModel.Design.Serialization.
CodeDomSerializerException 产生序列化错误的行号信息 无
ComponentModel.LicenseException 无法为组件授予许可证 无
ComponentModel.WarningException 异常被作为警告而不是错误处理 无
Configuration.ConfigurationException 配置设置中发生错误 无
Configuration.Install.InstallException 在安装过程的提交、回滚或卸载阶段发生错误 无


ContextMarshalException 尝试通过上下文范围封送对象失败 无
Data.DataException 使用 ADO.NET 组件时产生错误 Data.ConstraintException
Data.DeletedRowInaccessibleException
Data.DuplicateNameException
Data.InRowChangingEventException
Data.InvalidConstraintException
Data.InvalidExpressionException
Data.MissingPrimaryKeyException
Data.NoNullAlllowedException
Data.ReadOnlyException
Data.RowNotInTableException
Data.StringTypingException
Data.TypedDataSetGeneratorException
Data.VersionNotFoundException
Data.DBConcurrencyException 在升级操作中,DataAdapter 确定受影响的行数等于零 无
Data.SqlClient.SqlException SQL Server 返回警告或错误 无
Data.SqlTypes.SqlTypeException Data.SqlTypes 的异常基类 Data.SqlTypes.SqlNullValueException
Data.SqlTypes.SqlTruncateException
Drawing.Printing.
InvalidPrinterException 使用无效的打印机设置尝试访问打印机 无
EnterpriseServices.
RegistrationException 检测到注册错误 无
EnterpriseServices.Serviced
ComponentException 在运行的组件上检测到错误 无
ExecutionEngineException 在公共语言运行时的执行引擎上存在内部错误 无
FormatException 参数的格式不符合调用方法的参数规定 Net.CookieException
Reflection.CustomAttribute
FormatException
UriFormatException
IndexOutofRangeException 尝试访问其索引在数组范围之外的数组元素 无
InvalidCastException 无效的强制类型转换或显式转换 无
InvalidOperationException 方法调用对于对象的当前状态无效 Net.ProtocolViolationException
Net.WebException
ObjectDisposedException
InvalidProgramException 程序包含无效的 Microsoft 中间语言或元数据 无
IO.InternalBufferOverflowException 内部缓冲区溢出 无
IO.IOException 发生 I/O 错误 IO.DirectoryNotFoundException
IO.EndOfStreamException
IO.FileLoadException
IO.FileNotFoundException
IO.PathTooLongException
Management.ManagementException 管理错误 无
MemberAccessException 尝试访问类成员失败 FieldAccessException
MethodAccessException
MissingFieldException
MissingMemberException
MissingMethodException
MulticastNotSupportedException 尝试组合两个无法组合的代理类型实例,两者的操作数都为非空引用 无


NotImplementedException 未执行要求的方法或操作 无
NotSupportedException 不支持所调用的方法,或者尝试在不支持所调用函数的流中进行读取、查找或写入 PlatformNotSupportedException
NullReferenceException 尝试取消引用空对象引用 无
OutOfMemoryException 内存不足以完成执行程序 无
RankException 将具有错误维数的数组传递给方法 无
Reflection.AmbiguousMatch
Exception 绑定方法时导致多个方法符合绑定条件 无
Reflection.ReflectionType
LoadException Module.GetTypes 方法导致模块中的一个或多个类无法加载 无
Resources.MissingManifest
ResourceException 主要程序集不包含非特定语言的资源,但它们又是必需的,因为缺少合适的辅助程序集 无
Runtime.InteropServices.
ExternalException 所有 COM 互操作异常和结构化异常处理异常的基本异常类型 ComponentModel.Design.
CheckoutException
ComponentModel.Win32Exception
Data.OleDb.OleDbException
Messaging.MessageQueueException
Runtime.InteropServices.COMException
Runtime.InteropServices.SEHException
Web.HttpException
Runtime.InteropServices.
InvalidComObjectException 使用了无效的 COM 对象 无
Runtime.InteropServices.
InvalidOleVariantTypeException 封送器遇到无法封送到管理代码的变体类型参数 无
Runtime.InteropServices.
MarshalDirectiveException 封送器遇到不支持的 MarshalAsAttribute 无
Runtime.InteropServices.
SafeArrayRankMismatchException 传入 SAFEARRAY 的名次与管理签名中指定的名次不匹配 无
Runtime.InteropServices.
SafeArrayTypeMismatchException 传入 SAFEARRAY 的类型与管理签名中指定的类型不匹配 无
Runtime.Remoting.RemotingException 远程操作时发生错误 Runtime.Remoting.Remoting
TimeOutException
Runtime.Remoting.ServerException 用于在客户端连接到无法产生异常的非 .NET 框架应用程序时传递异常 无
Runtime.Serialization.
SerializationException 序列化或反序列化过程中发生错误 无
Security.Crytography.
CryptographicException 加密操作过程中发生错误 Security.Cryptography.
CryptographicUnexpected
OperationException
Security.Policy.PolicyException 策略禁止代码运行 无
Security.SecurityException 检测到安全性错误 无
Security.VerificationException 安全策略要求代码的类型安全,而验证程序无法验证代码是否类型安全 无
Security.XmlSyntaxException XML 分析时出现语法错误 无
ServiceProcess.TimeoutException 指定的超时已过期 无
StackOverflowException 待定的方法调用太多,导致执行堆栈溢出 无

Threading.SynchronizationLockException 在代码的异步块中调用同步方法 无
Threading.ThreadAbortException 调用 Abort 方法 无
Threading.ThreadInterruptedException 在 WaitSleepJoin 状态时线程中断 无
Threading.ThreadStateException 方法调用的无效 ThreadState 中的线程 无
TypeInitializationException 围绕类初始化程序产生的异常而产生的包装 无
TypeLoadException 类型加载失败 DllNotFoundException
EntryPointNotFoundException
TypeUnloadedException 尝试访问已卸载的类 无
UnauthorizedAccessException 操作系统拒绝访问,因为存在 I/O 错误或特定类型的安全性错误 无
Web.Services.Protocols.SoapException 在 SOAP 上调用 XML Web 服务方法而导致错误 Web.Services.Protocols.
SoapHeaderException
Xml.Schema.XmlSchemaException    无
Xml.XmlException    无
Xml.Xpath.XpathException 处理 Xpath 表达式时发生错误 无
Xml.Xsl.XsltException 处理可扩展样式表语言 (XSL) 转换时发生错误 System.Xml.Xsl.XsltCompileException