一、异常
.Net Framework处理的是异常而不是错误。因此不再会有“错误处理”。
.Net的公共语言库并不生成 错误代码。CLR创建一个称为异常的特殊对象,该对象的属性和方法详细描述了异常情况以及
引起错误的具体原因
vb.net中新功能:历史调试,又称“黑盒记录器”。它允许测试人员在运行期间捕获当前状态信息。
尽管VB6中有On Error Goto,Resume、Err等,但强烈建议使用.Net内置的异常处理功能替代旧式语法
关于结构化异常处理与非结构化异常处理的区别参看:
http://blog.csdn.net/dzweather/article/details/10726115
二、.Net中异常处理
异常产生的实例(异常对象)是派生于System.Exception类的一个实例。
System.Exception有许多子类用于不同的情形 ,可以提供不同类型的异常信息。
异常对象常用的属性
HelpLink 一个表示异常的帮助链接的字符串(常为程序员自定义链接)
InnerException 返回一个引用内部(嵌套)异常的异常对象。(后面会详述)
Message 包含错误的字符串,适合于显示给用户
Source 一个包含生成错误的对象名称的字符串
StackTrace 只读属性,以文字字符串形式保存堆栈跟踪。
例:方法A调用方法B,且在B中发生异常,则堆栈跟踪就包含方法A与B
TargetSite 只读属性,用于保存抛出异常的方法(字符串形式)
异常对象常用的方法
GetBaseException 返回异常链接中的第一个异常
ToString 返回错误字符串,其中可能包含错误消息、内部异常以及堆栈跟踪等与错误有关的信息。
三、异常种类
.Net中有许多派生于Exception基类的异常对象,每个异常对象都适用于特定类型的异常。
异常有多种,不同的名称空间有不同功能的异常类。比如:
空间 类 描述
System InvalidOperationException 对象所处的状态不允许调用当前的对象方法时产生
System OutOfMemoryException 当没有足够的内存继续工作时产生
System.XML XmlException 尝试读无效的XML文件时产生
System.Data DataException 代表ADO.net组件产生的错误
四、Try....Catch 关键字
Try '可能发生错误的代码块
[ tryStatements ]
[ Exit Try ]
[ Catch [ exception [ As type ] ] [ When expression ] '捕捉
[ catchStatements ]
[ Exit Try ] ]
[ Catch ... ]
[ Finally
[ finallyStatements ] ] '最后处理
End Try
Try 用于可能出现错误的代码
Catch 用于异常处理,当发生异常会出现在此处。
如果后面有 When则表示有异常且When句为真是进入
Finally 无论是否有异常,均进入此块
Catch可以有多个,发生异常时会逐个比较,只有当其中一个符合时,就进入此块,且后面的Catch不再进入。
注意:上面返回的是5,最后value的值是6,原因在于退出Try时,都会执行Finally,除非它是用End进行结束的。
下面演示多个Catch,它会依次向下比较,只有符合时就进入该块,进入后,后面的Catch块不再进入。
Public Class Form1
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim i As Int32 = 4
Dim j As Int32 = 0
TextBox1.Text = GetTry(i, j)
End Sub
Private Function GetTry(ByVal a As Int32, ByVal b As Int32) As Int32
Dim c As Int32
Try
c = a \ b
Return c
Catch ex As DivideByZeroException '执行这个捕获,下个不执行
MessageBox.Show("first")
Catch ex As Exception
MessageBox.Show("second")
End Try
Return c
End Function
End Class
五、Throw 关键字
Catch并不能处理所有错误,因为有些异常无法预料。
为了把这个“无法预料”的异常捕捉到,可人为地抛出这样的异常,Throw就用于人为抛出这样的异常。
同异常一样,用了Throw,它后面的代码不再执行,因为异常会中断它们,要么被Catch捕捉,要么程序中断。
同前面一样,Throw也不能阻止Finally块的执行,即:在退出Try时仍然会执行Finally
Public Class Form1
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim sngAvg As Single
TextBox1.Text = GetAverage(0, 100)
End Sub
Private Function GetAverage(iItems As Int32, iTotal As Int32) As Single
Try
Dim sngAverage As Single
sngAverage = CSng(iTotal \ iItems)
MessageBox.Show("sucessful")
Return sngAverage
Catch exD As DivideByZeroException
MessageBox.Show("zero exception")
Throw exD '抛出异常,将结束整个捕获过程。此处会异常,因不再有捕获,程序会直接中断
MessageBox.Show("More throw,but never executed")
Catch exG As Exception
MessageBox.Show("generic exception")
Throw exG
Finally
MessageBox.Show("last step")
End Try
End Function
End Class
注意: 上面Throw不会再被捕捉,因为Catch只会捕捉同级别的Try内容。
除非在Catch中再嵌套Try,并将Throw置于子Try中
Throw还可用于抛出自己创建的异常对象。
因此可以定制自己的异常消息。如下,异常消息改为自己的:
Public Class Form1
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim sngAvg As Single
TextBox1.Text = GetAverage(0, 100)
End Sub
Private Function GetAverage(iItems As Int32, iTotal As Int32) As Single
If iItems = 0 Then
Dim OurOwnExcption As New ArgumentException("iItems cannot be zero.") '自定义异常信息
Throw OurOwnExcption
End If
Try
Dim sngAverage As Single
sngAverage = CSng(iTotal \ iItems)
MessageBox.Show("sucessful")
Return sngAverage
Catch exD As DivideByZeroException
MessageBox.Show("zero exception")
Throw exD '抛出异常,将结束整个捕获过程。此处会异常,因不再有捕获,程序会直接中断
MessageBox.Show("More throw,but never executed")
Catch exG As Exception
MessageBox.Show("generic exception")
Throw exG
Finally
MessageBox.Show("last step")
End Try
End Function
End Class
六、嵌套Try结构
由于异常可能在Try结构中Catch中,为了进一步捕获异常,可以Catch块中再次套用Try结构。
1、 InnerException和TargetSite
TargetSite属性,表示异常所发生处位置哪个方法,这个方法用字串形式表示 。
InnerException属性用于存储异常的跟踪信息。中文意:内部异常。是什么意思呢?
A->B
上面最初是A引发了异常,这个异常又触发了异常B。那么B的内部异常就是A。因为外表看到常是异常B,用这个属性来查看异常A。
这种一层一层产生的一系统异常,可以用Exception对象的InnerException属性,将异常放入栈中,代以后引用。
当一个异常进入堆栈时,前一个Exception对象就变成了堆栈的内部异常。
这是什么意思呢?
如图,当没有任何异常时,上面堆栈中异常为空,如果有了A就会进入堆栈,由于前面没有异常,所以A的内部异常即innerException就是空。
如果又发生了异常B,B就会再次进入堆栈,此时异常A就会变成异常B的内部异常InnerException。
同理,又发生了C,C异常进入栈中,此时B又成了C的内部异常。以此类推。
Public Class Form1
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
HandlerExample()
End Sub
Private Sub HandlerExample()
Dim intX As Int32
Dim intY As Int32
Dim intZ As Int32
intY = 0
intX = 5
Try
intZ = CType(intX \ intY, Int32)
Catch objA As System.DivideByZeroException
Try
Throw (New Exception("==0 as divisor==", objA))
Catch objB As Exception
Dim sError As String
sError = "My Message:" & objB.Message & vbCrLf & vbCrLf
sError &= "Inner Exception Message:" & objB.InnerException.Message & vbCrLf & vbCrLf
sError &= "Method Error Occurred:" & objB.TargetSite.Name
MessageBox.Show(sError)
End Try
Catch
MessageBox.Show("Caught any other errors")
Finally
MessageBox.Show(Str(intZ))
End Try
End Sub
End Class
上面例子不是很好,因为在Throw构造的异常中,是把objA本身就作为了内部异常进构建,再来显示内部 异常。
下面是微软的一个例子:
Imports System
Public Class MyAppException
Inherits ApplicationException
Public Sub New(message As [String])
MyBase.New(message)
End Sub
Public Sub New(message As [String], inner As Exception)
MyBase.New(message, inner)
End Sub
End Class
Public Class ExceptExample
Public Sub ThrowInner()
Throw New MyAppException("First===")
End Sub
Public Sub CatchInner()
Try
Me.ThrowInner()
Catch e As Exception
Throw New MyAppException("Second===", e)
End Try
End Sub
End Class
Public Class Test
Public Shared Sub Main()
Dim testInstance As New ExceptExample()
Try
testInstance.CatchInner()
Catch e As Exception
Console.WriteLine("In Main catch block. Caught: {0}", e.Message)
Console.WriteLine("---------------------")
Console.WriteLine("Inner Exception is {0}", e.InnerException)
End Try
Console.Read()
End Sub
End Class
这个例子较好地显示了内部异常。结果如下:
2、Source、StackTrace
发生异常时,首先要确定的是异常的名称和异常所处的位置,上面两个就是指出这两个属性的。
Private Sub HandlerExample()
Dim intX As Int32
Dim intY As Int32
Dim intZ As Int32
intY = 0
intX = 5
Try
intZ = CType(intX \ intY, Int32)
Catch objA As System.DivideByZeroException
objA.Source = "HandlerExample"
MessageBox.Show(objA.Source & objA.StackTrace) '显示位置
Finally
MessageBox.Show(Str(intZ))
End Try
End Sub
3、GetBaseException
前面InnerExcetion已经演示了一系统异常入栈的情况:
A->B->C
从外层只能看到异常C,但有时我们得知道最终是哪个引起的。就会从外层C追起:
找C的InnerException,它是B
找B的InnerException,它是A
找A的InnerException,它是空,就是它
当一个异常的InnerException为空时,这个异常就是最终产生的异常的地方,即BaseException
Private Sub HandlerExample()
Dim intX As Int32
Dim intY As Int32
Dim intZ As Int32
intY = 0
intX = 5
Try
intZ = CType(intX \ intY, Int32)
Catch objA As System.DivideByZeroException
Try
Throw New Exception("0 as divsior==", objA)
Catch objB As Exception
Try
Throw New Exception("new error", objB)
Catch objC As Exception
MessageBox.Show(objC.GetBaseException.Message.ToString)
End Try
End Try
Finally
MessageBox.Show(Str(intZ))
End Try
End Sub
4、HelpLink
发生异常时,获得的帮助链接,常人为设置:
Private Sub HandlerExample()
Try
Dim a As New Exception("an error")
a.HelpLink = "www.baidu.com"
Throw a
Catch ex As Exception
Shell("explorer.exe " & ex.HelpLink)
End Try
End Sub
七、事件日志
异常发生后,我们常用日志写入文件中。 windows事件日志为三种:系统日志、应用程序日志、安全日志
事件日志有5种类型:信息事件、错误事件、警告事件、审核成功事件、审核失败事件。
事件日志中有很多记录,每条记录被称为条目。
条目(记录)必须有Source属性,它由程序员分配给事件的字符串,以利于在日志中对事件分类。
一个条目要进入事件日志中,必须对这个事件源(产生这个条目的程序或系统服务等)进行注册,即把相应的字符串写进入。
如果没指定事件源(注意不是Source属性),直接写入事件条目的话,会发生错误。
它相当于商家进入售前必须进行工商注册一样。
EventLog就是位置System.Diagnostics空间的类,它用于操作事件日志。
EventLog事件:
EntryWritten 当事件写入日志时发生
EventLog方法:
CreatEventSource 为指定日志创建一个事件源
DeleteEventSource 删除一个事件源和与之相关联的记录项
WriteEntry 为指定的日志写入字符串
Exists 判断指定的事件日志是否存在
SourceExists 判断指定的源是否存在于日志中(下例中会用到)
GetEventLogs 检索指定计算机中所有事件日志的列表
Delete 删除整个事件日志
EventLog属性:
source 指定要写入的记录项的源
Log 用于指定写入民,有系统、应用程序和安全日志3类日志,如果不指定,则默认情况下写入系统日志。
Public Class Form1
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
loggingExample()
End Sub
Sub loggingExample()
Dim objLog As New EventLog()
'指定事件日志项的事件类型,即信息事件、错误事件、审核失败事件、审核成功事件、警告事件共5类
'事件类型只能是其中一种。因为事件查看器根据类型来确定显示的图标
Dim objLogEntryType As EventLogEntryType
Try
Throw New EntryPointNotFoundException()
Catch ex As Exception
'用共享方法检查是否存在
If Not EventLog.SourceExists("vb2012") Then '确定事件源是否已在本地计算机上注册
EventLog.CreateEventSource("vb2012", "System")
End If
objLog.Source = "Example"
objLog.Log = "System"
objLogEntryType = EventLogEntryType.Information
objLog.WriteEntry("Error: " & ex.Message, objLogEntryType)
End Try
End Sub
End Class
八、写入文件
上面是写入到日志中,还可以写入到自定义的文件中。
下面利用Debug类进行监听对象,用于收集、存储信息,并发送给文本文件。
StreamWriter用于流文件写入到硬盘中。
Sub loggingExample()
Dim objWriter As New IO.StreamWriter("D:\mytext.txt", True) '建立输出流
Debug.Listeners.Add(New TextWriterTraceListener(objWriter)) '添加监听对象
Try
Throw New EntryPointNotFoundException() '强制抛出入口点异常(如:没找到该方法)
Catch ex As Exception
Debug.WriteLine(ex.Message) '写入监听缓冲
objWriter.Flush() '清理当前编写器缓冲,并将所有缓冲数据写入基础流。释放资源
objWriter.Close()
objWriter = Nothing
End Try
End Sub