7.程序异常处理
Delphi对应用程序中产生的异常定义在SysUtils、DB、ComCtrl等多个库单元中。
7.1Object Pascal异常基类
Delphi中,所有异常的基类是Exception类。其主要属性和方法如下:
1.属性
-
HelpContext
HelpContext是ThelpContext类的实例,提供了与异常对象联系在一起的上下文相关帮助信息的序列号。该序列号决定当发生异常时,用户按F1键显示的异常错误的帮助信息。
-
Message
存储异常发生时的错误信息。可以通过该属性在提示错误对话框中显示错误信息字符串。
2.方法
-
Create(Const Msg: string)
该方法用来产生一个具有简单提示信息的对话框,对话框中的提示信息由Msg提供。
-
CreateFmt(Const Msg: string, Const Args: Array of Const)
该方法用来产生一个具有格式化字符串提示信息的对话框,格式化字符串由Msg和Args提供。如:CreateFmt('%dbu z %d..%d范围内', [100, 0, 99]),产生”100不在0..99范围内“。
-
CreateHelp(Const Msg: string, AhelpContext: Integer)
该方法产生一个具有简单提示信息和上下文帮助序列号的提示对话框,其中:Msg参数时显示在异常对话框中的提示信息,AhelpContext参数是异常错误信息上下文帮助序列号。
7.2Delphi内建异常类
Delphi内建异常类很多,下面表格列出常见的Delphi内建异常类:
异常类 | 引发条件 |
---|---|
EAbort | 程序遇到错误但不弹出对话框 |
EAccessViolation | 无效内存访问错误 |
EAssertionFailed | 执行Assert过程返回值是False |
EControlC | 在控制台中,按【Ctrl】+【C】组合键遇到错误 |
EConvertError | 在字符串或其他对象集转换时遇到错误 |
EDivByZero | 整数除数是0 |
EinOutError | 输入/输出错误 |
EIntOverflow | 整数运算时,结果超过变量定义范围 |
EMathError | 浮点运算出现错误 |
EOutOfMemory | 超出内存错误 |
EOverflow | 浮点运算返回结果超出变量定义范围 |
ErangeError | 整数数值超出变量定义范围,例如数组下标越界 |
EStackOverflow | 堆栈溢出错误 |
EWin32ErrorWindows | 错误 |
EzeroDivide | 浮点运算除数是0 |
EdatabaseError | 数据库错误,在数据库中定义 |
EupdateError | 数据集提供者错误,在数据库中定义 |
EADOError | ADO数据库错误,在adodb库中定义 |
EDBEngineError | 出现BDE错误,在dbtables库中定义 |
EnoResultSet | 没有成功打开一个数据集查询 |
7.3自定义异常类
虽然Delphi已经有很多内建的异常类,但有时满足不了应用需求,此时,可以自行定义异常类,自定义异常类必须继承内建异常类。以下举例说明校验年龄数据的正确性,年龄限制在0~99之间。
运行时界面如下图所示:
代码如下:
implementation {$R *.dfm} type VerifyAgeError = class(Exception) // 自定义异常类 function toAge(age: integer): integer; end; function VerifyAgeError.toAge(age: integer): integer; begin if (age >= 100) or (age < 0) then showmessage('年龄不符合实际!'); result := abs(age) mod 99; end; // 校验按钮事件处理 procedure TForm1.Button1Click(Sender: TObject); var a: integer; begin try a := strtoint(Edit1.Text); if (a >= 100) or (a < 0) then raise VerifyAgeError.Create('年龄错误'); messagedlg('OK', mtinformation, [mbok], 0); except on e: EConvertError do messagedlg(e.Message, mterror, [mbok], 0); on e: VerifyAgeError do begin messagedlg(e.Message, mterror, [mbok], 0); Edit1.Text := inttostr(e.toAge(strtoint(Edit1.Text))); end; end; end; // 退出按钮事件处理 procedure TForm1.Button2Click(Sender: TObject); begin close; end; end.
7.4触发异常
异常触发可以分为两种:一种是程序系统自动触发,另一种是执行raise指令触发。如上面的代码中,EConvertError异常就是由系统在执行字符串转换整型数值的时候自动触发的,而VerifyAgeError是当年龄不在指定范围内时通过raise指令手动触发的。
raise指令的语法格式:
raise 异常对象
再次举例说明,以下例子通过输入两次密码相同或不同的情况进行处理。
运行时界面如下图所示:
代码如下:
implementation {$R *.dfm} type EPasswordInvalid = class(Exception); procedure TForm1.Button1Click(Sender: TObject); begin if edit1.Text <> edit2.Text then raise EPasswordInvalid.Create('密码输入有误!') else begin showmessage('密码设置完成!'); end; showmessage('欢迎光临!'); end; end.
7.5异常捕获
在Object Pascal程序语言中,有两种用来处理异常的语句:
-
try...finally...end
-
try...except...end
1.try...finally...end
语法格式:
try <被保护语句> finally <异常处理语句> //无论异常发生否,都必须处理 end;
该语句处理异常,只要触发异常,系统自动捕捉被触发的异常,然后以信息对话框的方式显示异常信息,让程序避开发生异常的代码,然后继续向下执行。
这种异常处理结构一般用于保护系统资源分配等方面,它确保了无论try体内的代码是否发生异常,都需要由系统进行最后的统一处理一些对系统对象的正确处理。
和try…except…end不同,该结构的finally部分总被执行。在try-finally语句中,当try部分产生异常后,应用程序直接执行Finally部分的资源释放语句。
示例:
运行时界面如下图所示:
示例代码:
implementation {$R *.dfm} procedure TForm1.Button1Click(Sender: TObject); var i: ^integer; res, y: double; begin try new(i); i^ := strtoint(edit1.Text); y := 3.1415926; res := y/i^; label1.Caption := '3.1415926 div ' + inttostr(i^) + ' = ' + floattostr(res); finally showmessage('现在释放i^的内存'); dispose(i); end; end; end.
2.try...except...end
语法格式:
try <被保护语句> except 异常处理语句 //异常不发生,不处理 end; 或 try <被保护语句> except on <异常对象类型1> do <语句1> //捕获指定类型的异常对象,进行处理 ...... on <异常对象类型n> do <语句n> //捕获指定类型的异常对象,进行处理 else <语句n+1> //缺省的异常处理代码 end;
在try体内的代码发生异常时,系统将转向except部分进行异常的处理。这是Delphi处理异常的最基本的方式之一。try语句块指出了需要进行异常保护的代码。如果在这部分有不正常的事件发生,则引发一个异常对象。except是异常处理部分,被保护部分引发的异常对象将执行<异常处理语句>或由这部分代码捕获并进行处理。
使用该语句处理异常时,会根据异常的类型不同,对异常做不同的处理。
在前面的自定义异常代码示例中已经使用过该语句,所以在此不再举例。