第八章 异常处理
8.2异常对象
定义于System单元中的Exception类;
所有的异常都是类类型的对象;
一般情况下不需要自定义异常类,需要时可以查询帮助文档;
自定义异常类举例
type
EMyException = class(Exception)
FMessage: Integer;
Constructor Create(ID: Integer);
end;
理论上可以使用任意对象代码,但是不推荐;
EMyException实现举例
program Project2;
{$APPTYPE CONSOLE}
uses
SysUtils, Dialogs;
type
EMyException = class(Exception)
FMessage: Integer;
Constructor Create(ID: Integer);
end;
constructor EMyException.Create(ID: Integer);
begin
ShowMessage('EMyException is created, its ID is ' + IntToStr(ID));
end;
var
I: Integer;
begin
Readln(I);
try
raise EMyException.Create(1001);
except
on EMyException do ShowMessage('产生的异常已经被处理');
end;
Readln;
end.
8.3异常处理语句
try 语句1 except 语句2 [else 语句3] end;
语句3部分是可选部分;
执行过程:
1.执行语句1,没有异常产生则直接执行end后;
2.产生异常则绕过语句1中剩下未执行的部分,直接跳到语句2;
3.在语句中寻找合适的处理语句,如果没找到则到else;
4.如果else缺省,则当前try无法处理这个异常,程序将这个异常提交到上一层的try;
5.如果还不能处理,继续往上;
6.如果最上层无法处理则提交给Delphi自动插入的异常处理语句,
这样的结果往往是程序强行中断并退出;
语句2有两种形式
1.普通代码:break,exit等,无法搞定则提交给else,不会直接跳到上一层;
2.类似选择语句
except
on obj1: type1
do 语句1;
on obj2: type2
do 语句2;
...
type1和type2等都是异常类的名字;
obj1和obj2等是异常对象的名称,可以省略,如果省略,之后的冒号一并省略;
这段语句中,如果某个异常对象所属的类的名称是TE,
程序会将TE和type1比较,如果TE是type1的派生类或者type1本身,则执行语句1,不是则继续type2;
如果都没有合适的,则提交到else,如果else缺省,提交给上一层;
try…finally…end
产生异常直接到finally,也就是finally部分一定会执行;
在使用try...finally...
类型的异常处理语句时,try...finally
部分执行的exit例程会被当做一个异常来处理;
举例
uses
SysUtils, Dialogs;
begin
try
Exit;
Writeln('input a num :');
Readln(i);
Writeln(9 div i);
finally
ShowMessage('delphi');
end;
end.
这里exit并不会退出全部程序,只退出finally之前,然后显示delphi;
8.4手动触发异常
使用raise举例
begin
Writeln('input a num i:');
Readln(i);
try
try
Writeln(IntToStr(9 div i));
except
ShowMessage('Inner Except');
raise Exception.Create('error'); //here
end;
except
ShowMessage('Outer Except');
end;
end.
这里会连续显示两个对话框,分别是Inner和Outer;
因为内层处理了异常后,raise语句将当前异常移交给外层,所有外层也执行了;
在实际应用中,如果无法完整处理这个异常,可以处理一部分,然后用raise语句丢给外层;
举例
program Project2;
{$APPTYPE CONSOLE}
uses
SysUtils, Dialogs;
procedure ReRaiseErr(i: Integer);
begin
// := TComponent.Create(Self);
try
Writeln(5 div i);
except
ShowMessage('Run Error');
raise;
end;
end;
begin
try
ReRaiseErr(0);
except
ShowMessage('Error in ReRaiseErr');
end;
end.
先显示Run Error
,然后是Error in ReRaiseErr
;
raise可以放在程序的任何地方;
raise语句只能放在异常处理语句中;
这其实并不矛盾,因为整个程序的最外边会被包进一个异常处理语句;
8.5Abort语句
Abort例程可以自动创建一个EAbort异常类的对象;
EAbort直接继承于Exception,成为静默异常类,引发此类异常不会显示任何对话框提示用户;
即不会对程序有任何干扰;
举例
program Project2;
{$APPTYPE CONSOLE}
uses
SysUtils, Dialogs;
var
i: Integer;
begin
Writeln('input a i');
Readln(i);
try
try
Writeln(IntToStr(9 div i));
except on Exception do
Abort;
end;
except on EAbort do
ShowMessage('EAbort Exception');
end;
end.
这里只显示了EAbort Exception
;
因为abort不会有提示;
8.6套嵌的异常处理语句
以try…except…end;为例进行嵌套;
如上一节所示;
套嵌语句中,只有内层处理不了异常或者手动触发异常时,这个异常才会被提交给外层处理;
异常丢失举例
program Project2;
{$APPTYPE CONSOLE}
uses
SysUtils, Dialogs;
type
E1 = class(Exception)
end;
var
i: Integer;
begin
Writeln('input a i');
Read(i);
try
try
Writeln(IntToStr(9 div i)); //sentence x
except
raise E1.Create('Error Message');
end;
except on E1 do //attention
ShowMessage('Outer Exception');
end;
end.
注意到,原本的异常不见了,好像异常就是E1;
虽然这里的异常一定是Exception的派生类,而且一定不是E1对象,也不是E1派生类的对象;
但是手动触发了E1类型的异常,被外层捕获并处理;
原本的异常就不见了,这就是异常对象的丢失;