图像处理算法交流群群规(入群请先在此留言)

Posted 白马负金羁

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了图像处理算法交流群群规(入群请先在此留言)相关的知识,希望对你有一定的参考价值。

“人非草木,孰能无情?” 噢,不对,应该是“人非圣贤,孰能无过?” 编写程序的人当然也会犯错,通常我们把程序里的这种错误(或者异常)称为bug。给程序中的错误起名为bug的人正是享誉世界的女性计算机科学家、素有COBOL之母称号的格蕾丝·默里·霍珀(Grace Murray Hopper)。1947 年发生了一件著名的事故,人们从Mark II 计算机的一个继电器中发现了一只飞蛾。格蕾丝·霍珀便将那只飞蛾用带子绑在计算机日志(logbook)上,并在其边上注明“first actual case of bug being found。从此,人们便把计算机程序中发现的错误称为bug。可以说从计算机程序诞生的那天起,bug就一直如同计算机程序的影子一样,从来没有离开过。


“墨菲定律”(Murphy's Law)也告诉我们,凡是可能出错的事就会有很大几率会出错,或者说越是担心、越是想避免的事情越是会发生!程序设计何尝不是这样?由于大部分的程序代码是由人经过思考而编写出来的,虽然力求完美,但是要完全没有bug,其实也是相当困难的。一般来说,所谓“异常(Exception)”是指程序运行时所发生的不正常的情况。为了避免程序在运行时发生不正常的错误情况,就需要利用某种机制来捕捉到这些问题,并及时应对随之而来的麻烦,从而避免程序陷入无法继续执行的窘境


本文介绍Visual Basic中提供的异常处理机制,主要包括捕捉异常的 Try... Catch... Finally 结构和自行抛出异常的Throw方法等内容。


一、Try... Catch... Finally


Try... Catch... Finally 主要是提供给特定的程序代码块的所有可能错误的处理方式。其语法形式如下:

Try 
    '程序代码,包含执行时可能产生错误的程序代码
Catch 变量名称 As Exception [When 表达式]
    '当发生错误时程序处理代码
Finally
    '必须执行代码,不论错误是否发生,通常用来释放资源
End Try
需要说明的是,当加上When来加以判断时,只有在表达式为True时才会捕捉异常,若表达式为False则不会捕捉异常。此处所说的表达式必须能够隐式地转换为布尔值(Boolean),描述方式与一般的表达式相同。它与When关键词搭配使用,其作用是加强筛选异常的能力,提高程序的执行效率。

来看一个潜藏了某些错误的例子。下面的程序要求用户输入两个整数a和b,然后计算a÷b的商。显然,除数b是不应该为0的,一旦用户输入了一个为0的除数,程序就会面临崩溃。

Module Module1

    Sub Main()
        Dim a, b, c As Integer
        Console.Write("a = ")
        a = Console.ReadLine()
        Console.Write("b = ")
        b = Console.ReadLine()

        c = a \\ b
        Console.WriteLine("a / b = {0}", c)
        Console.ReadLine()
    End Sub

End Module
来看一下程序的执行结果。显然,当除数为0时,程序出问题了。


下面利用Try... Catch... Finally 结构来改写上述程序,一旦程序捕捉到除数为零的异常,它会把异常信息输出到控制台上。

Module Module1

    Sub Main()
        Dim a, b, c As Integer
        Console.Write("a = ")
        a = Console.ReadLine()
        Console.Write("b = ")
        b = Console.ReadLine()

        Dim DivideOrNot As Boolean = True
        Try
            c = a \\ b
        Catch ex As Exception
            Console.WriteLine(ex.ToString)
            DivideOrNot = False
        End Try

        If DivideOrNot = True Then
            Console.WriteLine("a / b = {0}", c)
        End If

        Console.ReadLine()
    End Sub

End Module
读者可以自行执行上述程序,这里不再赘述。


此外,可以看到在Catch关键词后面,程序定义了一个Exception类的实例用来接收异常。事实上,Exception是一个父类,可以用来捕捉所有的异常,在Visual Basic中,还定义了若干具体类型的异常类,下表总结了其中比较常用的几个。


回忆《Visual Basic快捷教程——流程控制 》一文中给出的“求使得前n项自然数之和不大于某个预设上限的最大之n值”的例子。程序要求用户在文本框中输入一个正整数作为求和的上限。显然,当用户输入不合法时,程序就会抛出“类型转换异常”。彼时程序中使用的是父类Exception。既然我们已经知道此处捕捉的异常类型应该是“类型转换异常”,所以使用 InvalidCastException 更为妥当,即将原程序中的相应代码段调整为如下形式。

    Try
        upperBound = TxtBox_upbound.Text
    Catch ex As InvalidCastException
        MsgBox("请在文本框中输入一个正整数作为求和的上限!", vbExclamation, "提示")
        Return
    End Try
最后来看一个求N的阶乘的程序。在这个示例代码中,程序会捕捉到不同类型的异常消息。

Module Module1

    Sub Main()

        Dim i, N, f As Short
        Do While (True)
            Try
                Console.Write("N = ")
                N = Console.ReadLine()
                f = 1
                For i = 1 To N
                    f = f * i
                Next
                Exit Do
            Catch ex1 As InvalidCastException
                Console.WriteLine("类型转换错误!")
            Catch ex2 As OverflowException
                Console.WriteLine("溢出错误!")
            Catch ex3 As Exception
                Console.WriteLine("其他错误!")
            End Try
        Loop

        Console.WriteLine("{0}! = {1}", N, f)
        Console.ReadLine()
    End Sub

End Module
上述程序的执行效果见下图所示。


当有异常发生时,程序会从Try语句中第一个Catch捕捉到的异常类开始检查是否符合条件。如果符合条件,则不再比较后面的Catch条件。因此若将上述代码中的父类异常Exception提到子类型异常的前面,那么程序就只会运行Exception异常处理,而不会执行InvalidCastException 或 OverFlowException异常处理。


二、通过Throw自行抛出异常


上一节给出的表格里提供了Visual Basic里比较常用的异常类型。除了这些以外,系统中的默认异常类型还有DllNotFoundException(找不到动态链接库)、NullReferenceException(对象未被引用)和KeyNotFoundException(指定索引键未找到)等。可见,系统提供的默认异常类型其实是多种多样的。但是,如果你不想采用系统默认的异常显示信息时,或者想自己定义程序来处理特殊异常,也可以通过Throw的方式来自定义显示异常信息。

现在还是通过一个例子来解释Throw的用法。我们需要用户输入1~12中的一个整数来作为月份,系统会据此返回对应月份的英文简写。显然,大于12或小于1的整数都超出了一年12个月份的范畴。此时程序需要提醒用户输入的代表月份的数字不合理。

Module Module1

    Sub Main()
        Dim month As Integer
        Do While (True)
            Try
                KeyInMonth(month)
                Exit Do
            Catch ex As ArgumentOutOfRangeException
                Console.WriteLine("不合理月份")
            Catch ex As Exception
                Console.WriteLine("其他错误")
            End Try
        Loop

        Console.WriteLine("The month you input is " &
            Microsoft.VisualBasic.Choose(month, "Jan.", "Feb.", "Mar.",
                                     "Apr.", "May", "Jun.", "Jul.",
                                     "Aug.", "Sep.", "Oct.", "Nov.", "Dec."))
        Console.ReadLine()
    End Sub

    Public Sub KeyInMonth(ByRef month As Integer)
        Console.Write("请输入月份(1 - 12): ")
        month = Console.ReadLine()
        If (month < 1 Or month > 12) Then
            Throw New ArgumentOutOfRangeException()
        End If
    End Sub

End Module
来分析一下上述程序的执行过程。Try 后面紧接着应该是(可能出现问题)的代码段。在上面这个程序里,Try 后面要完成的内容其实是给整型变量month赋值。通常在这个赋值的过程中,如果程序执行出现问题,系统会自动抛出一个异常,就像前面给出的那些例子一样,如果不想做额外的处理,那么就在代码段完结后,Catch (接住)一个异常,如果接到了则做相应的处理。当前,因为程序要求用户输入的月份应该是介于1~12的一个整数,那么当出现一个不符合该条件的输入时,要求系统抛出一个ArgumentOutOfRangeException,这个异常前面已经介绍过。在MSDN中,对它的解释为:The exception that is thrown when the value of an argument is outside the allowable range of values as defined by the invoked method。一旦Catch (接住)的异常是ArgumentOutOfRangeException类型的,注意程序会特别提醒用户输入的月份不合理。程序执行的情况如下图所示,可见程序达到了预期的效果。


通常,Throw语句都要跟条件判断结合来使用。它的作用就在于当满足某些自定义的条件时,可以抛出一个用户指定类型的异常。Throw语句的作用或者价值也就在于此,因为当程序遇到错误时,系统会自动产生异常(这种自动产生的异常其实也就是被动地抛出异常),而结合Throw语句,便可以根据用户的需求主动地抛出异常。注意这个“用户指定的类型”可以系统内置的类型,也可以用户自己定义的异常类型。关于自定义异常类型的方法后面还会有详细的介绍。


三、自定义异常类


Exception类是当前异常发生时所继承的基类(也成为父类),绝大多数异常对象都是派生于Exception类的实例。所以在编写程序时,通过捕捉Exception类的实例来处理异常是一种比较通常的做法(因此无需判断具体的异常类型就能很方便地捕捉到绝大部分的异常)。但是,如果想让程序更加Robust,那么对于可能出现的异常情况进行更加准确的判断会有利于细化具体的应对措施,这也应该是被推荐的做法。


大多数异常的类都继承自Exception类(或其衍生出来的类)。Exception类的常见子类包括ApplicationException和SystemException。像本文前面介绍过的ArgumentOutOfRangeException就继承自ArgumentException,而ArgumentException又继承自SystemException。下图是MSDN上列明的继承层次结构。注意System是它们的namespace。


需要说明的是,SystemException适合在运行时产生错误的情况下来使用。在应用程序中不建议使用SystemException的方式来捕捉异常,而应该使用ApplicationException来处理异常或抛出。正如前面所讲过的,为了便于采用更加细化的应对措施,系统异常和应用程序异常分开处理会比较好。


尽管Visual Basic中提供了很多具体的异常类来应对各种各样的问题,但是如果系统内置的异常类仍然不能满足需求的话,我们就可以使用继承机制来对系统内置的异常类进行扩展或增加功能,而且你也可以重载异常类的方法或属性,甚至增加新的方法或属性来实现更为复杂的异常处理功能。


作为一个例子,下面来改造之前给出的含有除零异常的程序。之前的例子中,使用系统内置的DivideByZeroException异常,在下面的代码中,采用自定义编写的MyCustomException。可见,MyCustomException继承自ApplicationException。在具体实现时,我们重载了含参数的初始化函数,并新增了一个名为ShowMsg()的成员函数。

Public Class MyCustomException
    Inherits System.ApplicationException

    Public Sub New()
        MyBase.New()
    End Sub

    Public Sub New(ByVal Message As String)
        MyBase.New(Message)
    End Sub

    Public Sub ShowMsg()
        Console.WriteLine(Message)
    End Sub

End Class

Module Module1

    Public Function DoDivide(ByVal a As Double, ByVal b As Double, ByRef c As Double) As Boolean
        If b = 0 Then
            Throw (New MyCustomException("Can't have zero divisor!"))
        End If
        c = a / b
        Return True
    End Function

    Sub Main()

        Dim a, b, c As Double

        Console.Write("a = ")
        a = Console.ReadLine()
        Console.Write("b = ")
        b = Console.ReadLine()

        Dim DivideOrNot As Boolean = False
        Try
            DivideOrNot = DoDivide(a, b, c)
        Catch ex As MyCustomException
            ex.ShowMsg()
        End Try

        If DivideOrNot = True Then
            Console.WriteLine("a / b = {0}", c)
        End If

        Console.ReadLine()
    End Sub

End Module
读者可以在完成编码后自行执行上述程序并观察结果。从这个例子中,可以总结出使用自定义异常类的基本方法。通常,自定义的异常类要继承自已有的内置异常类(例如Exception或ApplicationException等),然后通过一般的面向对象方法重载原有函数或增加新的成员变量及成员函数来实现特定的异常处理功能。然后再结合Throw语句在满足某些特定条件的情况下,适时抛出合适的异常,并捕捉异常。在Visual Basic,恰当地运用异常处理机制会大大提升程序的鲁棒性。



*本文中所使用的Visual Studio版本为2013。


(本文完)

以上是关于图像处理算法交流群群规(入群请先在此留言)的主要内容,如果未能解决你的问题,请参考以下文章

WPF 实现倒计时转场动画~

算法导论 红黑树 学习 删除

WPF 实现验证码控件

WPF 实现加速小火箭~

WPF 实现大转盘抽奖~

让一个模型处理多种数据的N种方法