VB.net学习笔记(十八)异常处理

来源:互联网 发布:杭州萤石网络 点评 编辑:程序博客网 时间:2024/03/29 07:44




一、异常

      

         .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 FunctionEnd 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 FunctionEnd 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 FunctionEnd 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 SubEnd Class

           上面再次抛出异常时,把objA作为内部异常。结果如下:

           


          上面例子不是很好,因为在Throw构造的异常中,是把objA本身就作为了内部异常进构建,再来显示内部 异常。

           下面是微软的一个例子:

Imports SystemPublic 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 SubEnd ClassPublic 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 SubEnd ClassPublic 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 SubEnd 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

          上面就是objA->objB->objC,所以BaseException就是ObjA,即除0的异常:

          










            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