第十二章 异常处理与程序调试(二).doc_第1页
第十二章 异常处理与程序调试(二).doc_第2页
第十二章 异常处理与程序调试(二).doc_第3页
第十二章 异常处理与程序调试(二).doc_第4页
第十二章 异常处理与程序调试(二).doc_第5页
已阅读5页,还剩11页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第十二章异常处理与程序调试(二) 12.3异常响应 异常响应为开发者提供了一个按自己的需要进行异常处理的机制。try except end形成了一个异常响应保护块。与finally不同的是:正常情况下except 后面的语句并不被执行,而当异常发生时程序自动跳到except,进入异常响应处理模块。当异常被响应后异常类自动清除。下面的例子表示了文件打开、删除过程中发生异常时的处理情况:uses Dialogs;varF: Textfile;beginOpenDialog1.Title := Delete File;if OpenDialog1.Execute thenbeginAssignFile(F, OpenDialog1.FileName);tryReset(F);if MessageDlg(Erase +OpenDialog1.FileName + ?,mtConfirmation, mbYes, mbNo, 0) = mrYes thenbeginSystem.CloseFile(F);Erase(F);end;excepton EInOutError doMessageDlg(File I/O error., mtError, mbOk, 0);on EAccessDenied doMessageDlg(File access denied., mtError, mbOk, 0);end;end;end.保留字ondo用于判断异常类型。必须注意的是:except后面的语句必须包含在某一个ondo模块中,而不能单独存在。这又是同finally不同的一个地方。12.3.1 使用异常实例上面所使用的异常响应方法可总结为如下的形式:on ExceptionType do响应某一类的异常这种方法唯一使用的信息是异常的类型。一般情况下这已能满足我们的需要。但我们却无法获取异常实例中包含的信息,比如异常消息、错误代码等。假设我们需要对它们进行处理,那么就必须使用异常实例。为了使用异常实例,需要为特定响应模块提供一个临时变量来保存它:on EInstance : ExceptionType do在当前响应模块中我们可以象使用一个普通对象那样来引用它的数据成员。但在当前响应模块之外不被承认。下面的代码用于获取异常消息并按自己的方式显示它:窗口中包括一个ScrollBar部件,一个Button部件procedure TErrorForm.Button1Click(Sender: TObject);begintryScrollBar1.Max := ScrollBar1.Min-1;excepton E: EInvalidOperation doMessageDlg(Ignoring Exception:+E.Message,mtInformation,mbOK,0);end;end;12.3.2 提供缺省响应在异常响应模块中,一般我们只对希望响应的特定异常进行处理。如果一个异常发生而响应模块并没有包含对它的处理代码,则退出当前响应模块,异常类仍被保留。为了保证任何异常发生后都能在当前响应模块中被清除,可以定义缺省响应:try程序正常功能excepton ESomething do响应特定异常else提供缺省响应end;由于else可以响应任何异常,包括我们一无所知的异常,因此在缺省响应中最好只包括诸如显示一个消息框之类的处理,而不要改变程序的运行状态或数据。12.3.3 响应一族异常诸如on ExceptionType do的异常响应语句不仅可响应本类异常,而且可以响应子类异常。对于象EIntError、EMathError等系统不会引发的异常,它们将只响应其子类异常。而对于象 on Exception do这样的语句将会对任何异常进行响应。下面一段代码对整数越界异常进行单独处理,而对其它整数异常进行统一处理:try整数运算excepton ERangeError do越界处理on EIntError do其它整数异常处理end;由于异常在处理后即被清除,因而上面的代码可保证不会使ERangeError异常被多次处理。假如颠倒两条响应语句的顺序,则ERangeError异常响应将永远没有被执行的机会。由于异常在处理后即被清除,因而当希望对异常进行多次处理时就需要使用保留字raise来重引发一个当前异常。下面的代码同时使用了异常响应和异常保护。异常响应用于设置变量的值,异常保护用于释放资源。当异常响应结束时利用raise重引发一个当前异常。varAPointer: Pointer ;AInt , ADiv: Integer;beginADiv := 0;GetMem ( APointer , 1024 );trytryAInt := 10 div ADiv ;excepton EDivByZero dobeginAInt := 0 ;raise;end;end; finallyFreeMem ( APointer , 1024 );end;end;上面一段代码体现了异常处理的嵌套。异常保护、异常响应可以单独嵌套也可以如上例所示的那样相互嵌套。12.3.5 自定义异常类的应用利用Delphi的异常类机制我们可以定义自己的异常类来处理程序执行中的异常情况。同标准异常不同的是:这种异常情况并不是相对于系统的正常运行,而是应用程序的预设定状态。比如输入一个非法的口令、输入数据值超出设定范围、计算结果偏离预计值等等。使用自定义异常需要:1.自己定义一个异常对象类;2.自己引发一个异常。12.3.5.1 定义异常对象类异常是对象,所以定义一类新的异常同定义一个新的对象类型并无太大区别。由于缺省异常处理只处理从Exception或Exception子类继承的对象,因而自定义异常类应该作为Exception或其它标准异常类的子类。这样,假如在一个模块中引发了一个新定义的异常,而这个模块并没有包含对应的异常响应,则缺省异常处理机制将响应该异常,显示一个包含异常类名称和错误信息的消息框。下面是一个异常类的定义:typeEMyException = Class(Exception) ;12.3.5.2 自引发异常引发一个异常,调用保留字raise,后边跟一个异常类的实例。假如定义:typeEPasswordInvalid = Class(Exception);则在程序中如下的语句将引发一个EPasswordInvalid异常:If Password CorrectPassword thenraise EPasswordInvalid.Create(Incorrect Password entered);异常产生时把System库单元中定义的变量ErrorAddr的值置为应用程序产生异常处的地址。在你的异常处理过程中可以引用ErrorAddr的值。在自己引发一个异常时,同样可以为ErrorAddr分配一个值。为异常分配一个错误地址需要使用保留字at,使用格式如下:raise EInstance at Address_Expession;12.3.5.3 自定义异常的应用举例下面我们给出一个利用自定义异常编程的完整实例。两个标签框(Label1、Label2)标示对应编辑框的功能。编辑框PassWord和InputEdit用于输入口令和数字。程序启动时Label2、InputEdit不可见。当在PassWord中输入正确的口令时,Label2、InputBox出现在屏幕上。此时Label1、PassWord隐藏。设计时,令Label2、InputEdit的Visible属性为False。通过设置PassWord的PassWordChar可以确定输入口令时回显在屏幕上的字符。自定义异常EInvalidPassWord和EInvalidInput分别用于表示输入的口令非法和数字非法。它们都是自定义异常EInValidation的子类。而EInValidation直接从Exception异常类派生。下面是三个异常类的定义。typeEInValidation = class(Exception)publicErrorCode: Integer;constructor Create(Const Msg: String;ErrorNum: Integer);end;EInvalidPassWord = class(EInValidation)publicconstructor Create;end;EInvalidInput = class(EInValidation)publicconstructor Create(ErrorNum: Integer);end;EInValidation增加了一个公有成员ErrorCode来保存错误代码。错误代码的增加提供了很大的编程灵活性。对于异常类,可以根据错误代码提供不同的错误信息;对于使用者可以通过截取错误代码,在try.except模块之外来处理异常。从以上定义可以发现:EInvalidPassWord和EInvalidInput的构造函数参数表中没有表示错误信息的参数。事实上,它们保存在构造函数内部。下面是三个自定义异常类构造函数的实现代码。constructor EInValidation.Create(Const Msg: String; ErrorNum: Integer);begininherited Create(Msg);ErrorCode := ErrorNum;end;constructor EInValidPassWord.Create;begininherited Create(Invalid Password Entered,0);end;constructor EInValidInput.Create(ErrorNum: Integer);varMsg: String;begincase ErrorNum of1:Msg := Can not convert String to Number;2:Msg := Number is out of Range;elseMsg := Input is Invalid;end;inherited Create(Msg,ErrorNum);end;对于EInvalidInput,ErrorCode=1表示输入的不是纯数字序列,而ErrorCode=2表示输入数值越界。口令检查是用户在PassWord中输入口令并按下回车键后开始的。实现代码在PassWord的OnKeyPress事件处理过程中:procedure TForm1.PassWordKeyPress(Sender: TObject; var Key: Char);constCurrentPassWord = Delphi;beginif Key = #13 thenbegintryif PassWord.text CurrentPassWord thenraise EInvalidPassWord.Create;Label2.Visible := True;InputEdit.Visible := True;InputEdit.SetFocus;PassWord.Visible := False;Label1.Visible := False;excepton EInvalidPassWord dobeginPassWord.text := ;raise;end; end;Key:=#0;end;end;同样,在InputEdit的OnKryPress事件处理过程中实现了输入数字的合法性检查:procedure TForm1.InputEditKeyPress(Sender: TObject; var Key: Char);varRes: Real;Code: Integer;beginif Key = #13 thenbegintryval(InputEdit.text,Res,Code);if Code 0 thenraise EInValidInput.create(1);if (Res 1) or (Res 0 thenChangeGrid;RecFileForm.Caption := FormCaption+ - +FileName;NewButton.Enabled := False;OpenButton.Enabled := False;CloseButton.Enabled := True;end;总之,利用异常响应编程的中心思想是虽然存在预防异常发生的确定方法,但却对异常的产生并不进行事前预防,而是进行事后处理,并以此来简化程序的逻辑结构。12.4程序调试简介Delphi提供了一个功能强大的内置调试器(Integrated Debugger), 因而对程序的调试不用离开集成开发环境(IDE)就可以进行。程序错误基本可以分为两类,即运行时间错和逻辑错。所谓运行时间错是指程序能正常编译但在运行时出错。逻辑错是指程序设计和实现上的错误。程序语句是合法的,并顺利执行了,但执行结果却不是所希望的。对于这两类错误,调试器都可以帮助你快速定位错误,并通过对程序运行的跟踪和对变量值的监视帮助你寻找错误的真正原因和解决错误的途径。程序调试的主要内容可以概括为如下的几方面:1.调试的准备和开始;2.控制程序的执行;3.断点的使用;4.检查数据的值。程序调试只有用户实际上机操作才能真正掌握。在这一节中我们主要对调试中的主要问题和一些关键点进行介绍。至于一些很细小的问题相信读者可以在上机实际应用中掌握,因而没有列出。12.4.1 调试的准备和开始在程序开发过程中程序编码和调试是一个持续的循环过程,只有在你对程序进行了彻底的测试后才能交付最终用户使用。为了保证调试的彻底性,在调试前应制定一个详细的调试计划。一般说来应该把程序划分为几个相对独立的部分,分别进行调试,以利于错误的迅速定位,确保每一部分程序都按设计的要求运行。调试计划准备好后就可以开始程序的调试。开始一个调试过程包括:1.编译时产生调试信息;2.从Delphi里运行你的程序。在程序调试过程中,程序的执行完全在你的控制之中。你可以在任何位置暂停程序的执行去检查变量和数据结构的值,去显示函数调用序列,去修改程序中变量的值以便观察不同值对程序行为的影响。12.4.1.1 产生调试信息要使用内部调试器必须选中Option| Environment菜单References页的Integrated Debugging检查框。缺省情况下该框被选中。在开始调试前需要使用Symbols Debug Information(调试符号信息)编译工程文件。调试符号信息包含了一个符号表,能够使调试器在程序的源代码与编译器产生的机器代码间建立联系。这样在程序执行中可以同时查看对应的源代码。Delphi 在缺省情况下自动产生调试符号信息。在集成开发环境中的开关选项是Option|project菜单Compiler Options页的Debug Information and Local Symbols检查框。当产生的调试符号信息供内部调试器使用时,编译器把调试符号表储存在每个相应的.dcu文件中。如果希望在集成环境外使用Turbo Debugger,则需要把调试信息储存在最终的 .exe文件中。为此需要选定Option|Project菜单Linker页的Include TDW Debug Info检查框。由于储存调试信息大大增加了执行文件的大小,因而调试完成后应重新生成一个不包含

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论