delphi高手突破之异常及错误处理,Delphi高手突破

Delphi高手突破(三) Delphi高等升级,delphi高手突破晋级

第 3章  极度及错误管理

孔武有力的顺序来自于科学的错误管理。
 
 
 
 
相信本人,总会有意想不到的……

Delphi  高手突破     
正就像是现实生活中我们不或然吉祥如意,你所写的代码也不容许每少年老成行都能得到正确
的实施。生活中相见比不上意的业务,管理好了,云开日出;管理倒霉,情形会越变越糟,
竟然一发而不可整理,后果难料。程序设计中意气风发律如此,所谓健康的次第,并不是不出错的
次第,而是在一念之差的气象下能很好地拍卖的程序。
故此,错误处理向来是前后相继设计领域的一个要害课题。而极度就是面向对象编制程序提供
的错误处精晓决方案。它是三个非凡好的工具,借使您选择了 OOP,选取了
Delphi,那么
特别也就改成你的绝世接收了。
要让你信服地选取这么些,须要付出一些理由。在本章中会令你领会明了地问询特别所
推动的补益。
3.1  异常的庐山面目目
怎么着是相当?为啥要用它?
在依赖函数的结构中,日常采纳函数再次回到值来注脚函数是不是成功执行,并交给错误类
delphi高手突破之异常及错误处理,Delphi高手突破。型等音讯。于是就能够发出如下方式的代码:
 
nRetVal := SomeFunctionToOpenFile();
 
if nRetVal = E_SUCCESSED then // 成功开辟
begin
  ……
end
else if nRetVal = E_FILE_NOT_FOUND then // 未有找到文件
begin
  ……
end
else if nRetVal = E_FILE_FORMAT_ESportageHighlander then // 文件格式错
begin
  ……
end
else then
begin
  ……
end
 
使用重临错误代码的点子是非常广阔的,不过接受那样的秘籍存在多个难题:
(1卡塔尔国形成冗长、冗杂的道岔组织(多量的 if 或 case
语句卡塔尔,使得程序流程调控变得
复杂,同期招致测量检验专门的工作的头眼昏花,因为测量试验必要走遍每种分支。
 
·50·

十分及错误管理
(2卡塔尔国大概会存在未有被拍卖的谬误(函数调用者若是不决断再次回到值的话卡塔尔国。
卓绝能够很好地化解以上七个难题。
所谓“格外”是指三个十二分类的对象。Delphi 的 VCL 中,全体特别类都派生于
Exception
类。该类申明了那么些的貌似表现、性质。最注重的是,它有三个 Message
属性能够报告异
常产生的原因。
抛出四个丰富即标记一个荒唐的产生。使用 raise
保留字来抛出二个那贰个对象,如:
3
raise Exception.Create(′An error occurred!′);
但须求重申的是,相当用来申明错误爆发,却并不因为错误暴发而发出十二分。发生异
常仅仅是因为遭受了 raise,在任什么时候候,纵然未有不当发生,raise
都将会促成十分的产生。 注意:非常的发出,仅仅是因为 raise,而非其余!
譬喻抛出非常,函数的代码就从那一个抛出处立刻回去,进而保险其下部的灵敏代码不
会得到实行。对于抛出十二分的函数本人来讲,通过特别从函数重回和常规从函数重临(执
行到函数末尾或遇到了
Exit卡塔尔国是尚未什么分别的,函数代码相通会从仓库弹出,局地轻便
对象(数组、记录等卡塔 尔(阿拉伯语:قطر‎会活动被清理、回笼。
选用抛出极度以管理意外景况,则能够确认保障程序主流程中的全体代码可用,而不要加
入繁缛的论断语句。
举例,函数 A抛出十分:
 
function A() : Integer;
vat
  pFile : textfile;
begin
  …… // 一些代码
  pFile := SomeFunctionToOpenAnFile();
  if pFile = nil then
raise Exception.Create(′Open file failed!′); // 文件张开失利抛出十二分
 Read(pFile, ……); // 读文件
  …… // 其余部分对文件的操作,当时得以确定保障文件指针有效
end;
 
函数
A的代码使得对文本展开的失误管理特别轻松。若是展开文件退步,则抛出四个
Exception
类的丰裕对象,函数登时回到,进而维护了以下对文本指针的操作不被执行。而
而后的代码能够纵然文件指针确定有效,进而令代码尤其玄妙。
生存中,大家每一日扔掉的垃圾堆都会有干净工人收拾、管理,不然生活条件中岂不随地
充满着垃圾?同样,抛出的不胜也需求被抓获和拍卖。假若函数 B 调用了函数
A,要捕获
其一文件展开失利的不得了,就供给在调用 A
在此之前先预设多个陷阱,那一个陷阱正是所谓的
“try…except 块”。
 
·51·

Delphi  高手突破     
先看一下函数 B 的代码:
 
procedure B();
begin
  …… // 一些代码
 try
   A(); // 调用A
   SomeFunctionDependOnA(); // 信赖于A的结果的函数
 Except
   ShowMessage(′some error occured′); // 嘿嘿,掉进来了,发生万分
 End;
  …… // 继续的代码
end;
 
A抛出的极度,会被 B所设的 try…except
所擒获。生龙活虎旦捕获到不行,就不再实践之后
的敏感代码,而是立时跳至 except 块试行错误处理,管理实现后再继续实践整个
try 块之
后的代码。程序流程的调整权被留在了函数 B。
设若不希罕本人整理垃圾,由此在 B 中并不曾预设 try…except
块的话,则极其会被继
续抛给 B 的调用者,而只要 B
的调用者同样不辜负义务,则丰富会被接续像踢足球同样被踢
给更上层的调用者,以此类推。不过,不用怀恋,大家有三个大管家,大家都实际不是的烫手
红山药,它会帮大家整理,那正是——VCL(Delphi 的应用程序框架卡塔尔。
因为 VCL 的框架使得所编纂的漫天应用程序被包在多个大的 try…except
中,不论什
么未有被拍卖的要命,最后都会被它所破获,并将程序流程重返到最外层的音信循环中,
决无脱漏!那也正是干吗会看精湛多用 Delphi
所编写的但并不专门的学问的小软件有的时候会跳出
三个告诉错误的对话框(如图 3.1
所示卡塔尔国。爆发这么的情状相应责备软件的编辑未有很
好地处理错误,但多少不亮堂非凡机制的技士平日会信心胡说 Delphi
编写的程序怎么可以会有那
样的景况发生。其实现身那几个提示,应该多谢VCL的非常机制让程序能够三番五遍运转并不是
“违法终止”。
 
图3.1  格外被VCL所擒获 注意:VCL 用叁个大的 try…except
将代码包裹起来!
进而,在 VCL
框架中不会有不被处理的充裕,换句话说,约等于不会有不被拍卖的错
误(尽管作者说过十三分并不等于错误卡塔 尔(英语:State of Qatar)。对那么些的捕获也相当轻巧,不见了一大堆的
if 或
 
·52·

老大及错误管理
case,程控流程的走向也就极度清晰明了了,那是给测验职员带来的好音信。
3.2  成立本身的那几个类
老大机制是全然融合面向对象的系列的,所以特别类和平日类同样具有继续和多态的
3
天性。其实,十分类和日常类并不曾什么分别。
Object Pascal的运维时非常基类是
Exception,VCL中有所极度类都应有从它派生。当
然,Object 帕斯Carl 语言并不分明如此,能够用 raise
抛出任何除轻易类型之外的类类型的对
象,try…except 相像可以捕获它,在十二分管理后生龙活虎致会自行析构、回笼它,只是
Exception
概念了超级大部风味。既然外人已经为我们思谋了一个好用的、康健的
Exception,当
然未有理由实际不是它。
或是读者也早就注意到,全体 VCL
的非常产生时,弹出的警戒对话框都带有风度翩翩段有价
值的对于那多少个的发出原因的叙说(正如图 3.1 中的“”is not a valid integer
value”卡塔 尔(英语:State of Qatar)。这段
陈述对于 debug 职业是不行平价的。它就是源于于 Exception 类的
Message属性,全体非常
类被创设时都不得不提交三个失误描述。因而,在概念、使用本人的极度类时,也要付出风流罗曼蒂克
个不会让人吸引的、明白说出错误原因的 Message 属性。 注意:从
Exception派生自身的要命类!
下边以四个示范程序来演示怎样定义、使用本人的万分类,其代码及可实践文件可在
配书光盘的 exception 目录下找到。
程序运维后的界面如图 3.2 所示。
 
图3.2  自定义特别类演示程序分界面
该程序的周转分界面十一分丰裕地体现了第 1 章所说的“简单性”原则。分界面上只有 3

开关,先看上边三个(另一个“try…finally”开关先不表达,留待 3.3
节讲明卡塔尔国。三个仿照
张开文件时爆发“找不到文件”的荒唐,三个模仿爆发“文件格式错”的荒谬。所谓模拟
发出错误,就是在并未当真爆发错误的事态下抛出特别,使得编写翻译器以为爆发了不当,
即单击那四个按键后,程序会独家抛出相应的这些。
先是要定义三种错误所对应的特别类。它们的定义和兑今后 ExceptionClass.pas
单元
中。该单北魏码清单如下:
 
·53·

Delphi  高手突破     
unit ExceptionClass;
 
interface
 
uses SysUtils, Dialogs;
 
Type
 
  EFileOpenFailed = class(Exception) //
定义二个文本张开战败的通用卓殊类
 public
    procedure Warning(); virtual; abstract;
 end;
 
  EFileNotFound = class(EFileOpenFailed) // 细化文件展开失败的不得了
 public
    procedure Warning(); override;
 end;
 
  EFileFormatErr = class(EFileOpenFailed) // 细化文件打开退步的特别
 public
    procedure Warning(); override;
 end;
 
implementation
 
{ EFileNotFound }
 
procedure EFileNotFound.Warning;
begin
 ShowMessage(‘真是匪夷所思,竟然找不到文件!’);
end;
 
{ EFileFormatErr }
 
procedure EFileFormatErr.Warning;
begin
 ShowMessage(‘更不可思议的是,文件格式不对!’);
end;
 
end.
 
大家先定义了二个评释张开文件退步的可怜基类
EFileOpenFailed,并给它注脚了一个
 
·54·

丰裕及错误管理
虚幻方法
Warning。然后又细化了不当的原由,进而派生出多个可怜类——EFileNotFound、
EFileFormatErr,它们都持之有故落到实处了 Warning 方法。
在应用程序的主Form(Form1卡塔 尔(阿拉伯语:قطر‎中,定义三个模仿爆发错误并抛出极度的SimulateError()
措施来效仿发生错误、抛出极度。
接下来定义三个 ToDo()方法来调用会吸引那些的 SimulateError(),何况用 Try
将其擒获
开展丰盛管理。
3
终极在七个按键的 OnClick()事件中,调用 ToDo()方法。
其代码清单如下:
 
unit Unit1;
 
interface
 
uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls,
  Forms, Dialogs, StdCtrls;
 
type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Label1: TLabel;
    Button3: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
      procedure SimulateError(Button : TObject);
      procedure ToDo(Button : TObject);
  end;
 
var
  Form1: TForm1;
 
implementation
 
uses ExceptionClass;
 
 
·55·

Delphi  高手突破     
{$R *.dfm}
 
procedure TForm1.SimulateError(Button : TObject);
begin
    if Button = Button1 then
        raise EFileNotFound.Create(‘File Not Found’)
    else if Button = Button2 then
        raise EFileFormatErr.Create(‘File Format Error’)
    else // Button = Button3
        raise Exception.Create(‘Unknonw Error’);
end;
 
procedure TForm1.Button1Click(Sender: TObject);
begin
    ToDo(Sender);
end;
 
procedure TForm1.Button2Click(Sender: TObject);
begin
    ToDo(Sender);
end;
 
procedure TForm1.ToDo(Button : TObject);
begin
    try
        SimulateError(Button)
    except
        on E : EFileOpenFailed do
            E.Warning();
        on E : Exception do
            ShowMessage(E.Message);
    end;
end;
 
procedure TForm1.Button3Click(Sender: TObject);
var
    AStream : TMemoryStream;
begin
    AStream := TMemoryStream.Create();
 
    try
        SimulateError(Sender);
 
·56·

老大及错误管理
    finally
        AStream.Free();
    end;
end;
 
end.
  3
程序运营后,当单击分界面上边的七个开关之一时,都会调用 ToDo 方法。而在
ToDo
艺术中,由于 SimulateError
被调用而吸引一个十三分,固然并从未真的产生打开文件破绽百出,
但的确抛出了特别。那再度表明了,非常只是用来注明错误,而并不等同错误。
程序中,大家定义了叁个注脚张开文件失利的不行基类
EFileOpenFailed,以至多个派
生的分外类——EFileNotFound、EfileFormatErr。那样定义极其类框架,给错误管理部分带
来了愈来愈多的灵活性。那是多态性给大家的又三个好处。能够自由选用必要捕获的老大的“精
度”。也正是说,借使顾客特别保养发生错误的求实原因,则能够捕获每一种最尾部的十分
类;而只要只关心是还是不是发生了展开文件的荒唐,那么能够只捕获
EFileOpenFailed类;若关
心的只是是或不是有不当发生,则只需捕获 Exception 就可以了。
在 SimulateError 的调用之外,设置了
try…except,那么它所吸引的特别都会被擒获。
将“精度”更“细”的相当类的管理代码放在前边,而把“精度”较“粗”的百般类的处
理代码放在后边。假设相反,则持有特别都会被
Exception的管理代码捕获,而任何的不行
类的拍卖代码则恒久都还未机遇实行了。
Exception
程序演示了一个相当的小的、自定义的不胜类框架的概念、完成及运用。“麻雀
虽小,麻雀虽小”,它交给了黄金年代种在融洽程序中破绽超级多的破获、管理的思绪。
3.3  try…finally
近年来曾经知晓,在函数中抓住这些将招致函数的平常重临,因而函数栈中的局部简单
对象(数组、记录等卡塔 尔(阿拉伯语:قطر‎会博得释放。同期也亮堂了,在 Object Pascal中有所的类对象都在
堆中被组织,编写翻译器不会在退出函数时自动调用它们的析构函数,那么如何确定保障具有的局
类型对象也能被放出吧?
Object 帕斯Carl引进了特别的 try…finally 来化解那些主题素材。
try…finally
块帮您作保一些器重的代码在无论是不是发生非常的气象下都能被实行,那
些代码位于 finally和 end之间。
再一次张开 Exception 程序,今后来看一下没用过的第 3 个按键。为它的 Click
事件加多
日常来说的代码:
 
procedure TForm1.Button3Click(Sender: TObject);
var
  AStream : TMemoryStream;
 
·57·

Delphi  高手突破     
begin
  AStream := TMemoryStream.Create();
 
 try
   SimulateError(Self);
 finally
   AStream.Free();
 end;
end;
 
它首先创建了叁个内部存款和储蓄器流对象,以模拟该函数申请了生龙活虎部分系统财富。然后依然调用
了 SimulateError 方法,可是此番 SimulateError 抛出的是三个 Exception
至极。但在那把
内存流对象的死灭工作放在了 finally
爱护之中,由此保障该对象的释放。可以友善单步
跟踪试一下,无论在发生极其(即调用了
SimulateError卡塔尔国的意况下,仍旧健康退出(不
调用 SimulateError 或将 SimulateError 的调用改为
Exit卡塔尔国的状态下,AStream.Free()都会得
到执行。
再就是具有 try…except 和 try…finally,应该说是 Delphi
程序猿的大器晚成种幸运,值得庆幸。
只是,大家想得到的会更加的多,会愿意具备
 
try
  ……
except
  ……
finally
 
这般的构造,只是近来还得不到知足。即使能够用
 
try
 try
   ……
 except
   ……
 end
finally
  ……
end;
 
来替代,但眼看不及所企望的那么组织雅观和高贵。那不得不说是生机勃勃种可惜,让大家寄希
望于下二个 Delphi 版本吧!
 
·58·

十分及错误管理
3.4  构造函数与丰裕
以此话题在 C++社区中时常会被聊到,而在 Delphi
社区中好似根本未有人注意过,也
许由于语言的风味而使得 Delphi 技师不必关切那个标题。但本人想,Delphi
技术员也相应
3
对该难题负有领会,知道语言为我们提供了怎样而使得大家这么轻巧,不必理会它。正所
谓“身在福中须知福”。
大家领悟,类的构造函数是未曾再次回到值的,因而只要构造函数构造对象战败,则不行
能依附重临错误代码来灭亡。那么,在程序中什么标志构造函数的波折呢?最“标准”的
艺术正是:抛出二个卓殊。
构造函数战败,意味着对象的布局战败。那么抛出特别之后,那些“半死不活”的对
象会被哪些管理啊?
在那,读者有供给先对 C++对这种气象的管理方式有叁个叩问。

C++中,构造函数抛出极度后,析构函数不会被调用。这种做法是有理的,因为此
时对象并不曾被完好构造。
要是构造函数已经做了后生可畏部分诸如分配内部存储器、张开文件等操作,那么
C++类须要有和好
的分子来记住做过什么动作。当然,那样做对于类的完结者来讲极度麻烦。因而,日常C++
类的完成者都防止在构造函数中抛出非常(能够提供三个诸如 Init 和 UnInit
的成员函数,
由构造函数或类的客商去调用它们,以管理开端化战败的状态卡塔尔国。而每一本
C++的经典著
作所提供的方案都以选用智能指针(STL 的正式类 auto_ptr)。
在 Object 帕斯Carl 中,那么些标题变得特别轻便,程序猿不必为此冥思遐想。假诺Object
帕斯Carl的类在构造函数中抛出特别,则编译器会自动调用类的析构函数(由于析构函数不
同意被重载,能够确认保证只有惟意气风发三个析构函数,由此编写翻译器不会吸引于三个析构函数之中卡塔 尔(阿拉伯语:قطر‎。
析构函数中平时会析构成员对象,而 Free()方法保障了不会对 nil
对象(即未有被创建的成
员对象卡塔尔国调用析构函数,由此在驱动代码简洁精粹的前提下,又保障了晋城。
以下的主次演示了构造函数中抛出特别后,Object Pascal编写翻译器所作的拍卖措施。
首先定义 TMyClass: 
 
type 
  TMyClass = class
 private
    FStr : PChar; // 字符串指针
 public
   constructor Create();
    destructor Destroy(); override;
 end;
 
接下来达成 TMyClass,并让它的构造函数中抛出极其:
 
·59·

Delphi  高手突破     
constructor TMyClass.Create();
begin
  FStr := StrAlloc(10); // 构造函数中为字符串指针分配内部存储器
 StrCopy(FStr, ‘ABCDEFGHI’);
  raise Exception.Create(‘error’); // 抛出特别,未有理由
end;
 
destructor TMyClass.Destroy();
begin
 StrDispose(FStr); // 析构函数中自由内部存款和储蓄器
 WriteLn(‘Free Resource’);
end;
 
末段,编写程序主流程的代码。主流程中率先成立 TMyClass 类的实例:
 
var
  Obj : TMyClass;
  i : integer;
begin
 try
    Obj := TMyClass.Create();
    // Obj.Free(); //
不调用析构函数,但发生非常时,编写翻译器自动调用了析构函数
   WriteLn(‘Succeeded’);
 except
    Obj := nil;
   WriteLn(‘Failed’);
 end;
 
 Read(i); // 暂停显示器,以便观望运营结果
end.
 
这段代码中,成立 TMyClass 类的实例时遇上了劳动,因为 TMyClass
的构造函数抛出
了十三分,但这段代码施行结果却是:
 
Free Resource
Failed
 
出现了“Free
Resource”,表明产生非常后,析构函数被调用了。而那多亏在结构函
数抛出十一分之后,编写翻译器自动调用析构函数的结果。
由此,假使类的认证文书档案或类的小编告知您,类的构造函数恐怕会抛出十分,那就要
记得用 try…except 包住它!
 
·60·

十三分及错误管理
C++与 Object Pascal对于构造函数抛出非常后的不如管理情势,其实正是二种语言的
两全思想的反映。C++秉承 C
语言的风格,重视功用,一切交给技术员来调控,编写翻译器不
做多余动作;Object Pascal 世袭 帕斯Carl的风骨,敬性格很顽强在荆棘载途或巨大压力面前不屈程序的美学意义,编写翻译器协理技术员
做到复杂的劳作。
3.5  小    结  3
拾壹分是面向对象编制程序带给的非凡好的工具,不加以利用是很心痛的。然而,正如任何
都有个“度”,滥用至极也是不可取的。使用特不是一向不代价,它会大增程序的担负,
编排若干 try…except 和编辑数以千计的 try…except
之间是有相当的大分别的。
再者,也无需过于惊惧由它所带给的担任。其实,既然已经使用了
Delphi,其实就已
经在运用特别了,只怕只是本身还不理解。听听 Chalie Calverts
的忠告:“在如同有用的
时候,就活该运用 try…except
块。不过要试着让自个儿对这种技巧的热心肠不要太过分”。

 

)
Delphi高档晋级,delphi高手突破进级 第 3章非凡及错误处理强壮的先后来自周振天确的错误管理。 相信笔者,总会有意料之外的…

怎么样是特别?为什么要用它?

所谓“十分”是指一个老大类的目的。Delphi的VCL中,全部特别类都派生于Exception类。该类申明了拾分的平日表现、性质。最着重的是,它有三个Message属性能够告诉充足发生的因由。

但必要重申的是,万分用来表明错误发生,却并不因为错误发生而发出特别。爆发极其仅仅是因为碰着了raise,在其余时候,尽管对的误发生,raise都将会引致相当的发出。分外的产生,仅仅是因为raise,而非其余!接纳抛出卓殊以管理意外景况,则足以保险程序主流程中的全数代码可用,而不须求参与烦琐的判定语句。举例,函数A抛出非常:

 

[delphi] view
plain copy

 

  1. function A() : Integer;  
  2. vat  
  3. pFile : textfile;  
  4. begin  
  5. …… // 一些代码  
  6. pFile := SomeFunctionToOpenAnFile();  
  7. if pFile = nil then  
  8. raise Exception.Create(′Open file failed!′); // 文件张开失利抛出分外  
  9. Read(pFile, ……); // 读文件  
  10. …… // 其余部分对文件的操作,当时能够确认保障文件指针有效  
  11. end;  

函数A的代码使得对文本展开的失误管理极度轻便。如若展开文件战败,则抛出叁个Exception类的不得了对象,函数登时回去,进而爱戴了以下对文件指针的操作不被施行。抛出的异常也急需被抓走和拍卖。借使函数B调用了函数A,要捕获那一个文件打开失利的十二分,就要求在调用A早前先预设多少个陷阱,那个陷阱正是所谓的“try…except块”。先看一下函数B的代码:

相关文章