![DELPHI基础教程:异常处理与程序调试(二)[1],第1张 DELPHI基础教程:异常处理与程序调试(二)[1],第1张](/aiimages/DELPHI%E5%9F%BA%E7%A1%80%E6%95%99%E7%A8%8B%EF%BC%9A%E5%BC%82%E5%B8%B8%E5%A4%84%E7%90%86%E4%B8%8E%E7%A8%8B%E5%BA%8F%E8%B0%83%E8%AF%95%EF%BC%88%E4%BA%8C%EF%BC%89%5B1%5D.png)
异常响应
异常响应为开发者提供了一个按自己的需要进行异常处理的机制 try …except …end形成了一个异常响应保护块 与finally不同的是 正常情况下except 后面的语句并不被执行 而当异常发生时程序自动跳到except 进入异常响应处理模块 当异常被响应后异常类自动清除
下面的例子表示了文件打开 删除过程中发生异常时的处理情况
uses Dialogs
var
F: Textfile
begin
OpenDialog Title := Delete File
if OpenDialog Execute then
begin
AssignFile(F OpenDialog FileName)
try
Reset(F)
if MessageDlg( Erase +OpenDialog FileName + ?
mtConfirmation [mbYes mbNo] ) = mrYes then
begin
System CloseFile(F)
Erase(F)
end
except
on EInOutError do
MessageDlg( File I/O error mtError [mbOk] )
on EAccessDenied do
MessageDlg( File access denied mtError [mbOk] )
end
end
end
保留字on…do用于判断异常类型 必须注意的是 except后面的语句必须包含在某一个on…do模块中 而不能单独存在 这又是同finally不同的一个地方
使用异常实例
上面所使用的异常响应方法可总结为如下的形式
on ExceptionType do
{响应某一类的异常}
这种方法唯一使用的信息是异常的类型 一般情况下这已能满足我们的需要 但我们却无法获取异常实例中包含的信息 比如异常消息 错误代码等 假设我们需要对它们进行处理 那么就必须使用异常实例
为了使用异常实例 需要为特定响应模块提供一个临时变量来保存它
on EInstance : ExceptionType do …
在当前响应模块中我们可以象使用一个普通对象那样来引用它的数据成员 但在当前响应模块之外不被承认
下面的代码用于获取异常消息并按自己的方式显示它
{窗口中包括一个ScrollBar部件 一个Button部件}
procedure TErrorForm Button Click(Sender: TObject)
begin
try
ScrollBar Max := ScrollBar Min
except
on E: EInvalidOperation do
MessageDlg( Ignoring Exception: +E Message
mtInformation [mbOK] )
end
end
提供缺省响应
在异常响应模块中 一般我们只对希望响应的特定异常进行处理 如果一个异常发生而响应模块并没有包含对它的处理代码 则退出当前响应模块 异常类仍被保留
为了保证任何异常发生后都能在当前响应模块中被清除 可以定义缺省响应
try
{程序正常功能}
except
on ESomething do
{响应特定异常}
else
{提供缺省响应}
end
由于else可以响应任何异常 包括我们一无所知的异常 因此在缺省响应中最好只包括诸如显示一个消息框之类的处理 而不要改变程序的运行状态或数据
响应一族异常
诸如
on ExceptionType do
的异常响应语句不仅可响应本类异常 而且可以响应子类异常 对于象EIntError EMathError等系统不会引发的异常 它们将只响应其子类异常 而对于象
on Exception do
这样的语句将会对任何异常进行响应
下面一段代码对整数越界异常进行单独处理 而对其它整数异常进行统一处理
try
{整数运算}
except
on ERangeError do
{越界处理}
on EIntError do
{其它整数异常处理}
end
由于异常在处理后即被清除 因而上面的代码可保证不会使ERangeError异常被多次处理 假如颠倒两条响应语句的顺序 则ERangeError异常响应将永远没有被执行的机会
由于异常在处理后即被清除 因而当希望对异常进行多次处理时就需要使用保留字raise来重引发一个当前异常
lishixinzhi/Article/program/Delphi/201311/25188
下面的代码同时使用了异常响应和异常保护 异常响应用于设置变量的值 异常保护用于释放资源 当异常响应结束时利用raise重引发一个当前异常
var
APointer: Pointer
AInt ADiv: Integer
begin
ADiv :=
GetMem ( APointer )
try
try
AInt := div ADiv
except
on EDivByZero do
begin
AInt :=
raise
end
end
finally
FreeMem ( APointer )
end
end
上面一段代码体现了异常处理的嵌套 异常保护 异常响应可以单独嵌套也可以如上例所示的那样相互嵌套
自定义异常类的应用
利用Delphi的异常类机制我们可以定义自己的异常类来处理程序执行中的异常情况 同标准异常不同的是 这种异常情况并不是相对于系统的正常运行 而是应用程序的预设定状态 比如输入一个非法的口令 输入数据值超出设定范围 计算结果偏离预计值等等
使用自定义异常需要
自己定义一个异常对象类
自己引发一个异常
定义异常对象类
异常是对象 所以定义一类新的异常同定义一个新的对象类型并无太大区别 由于缺省异常处理只处理从Exception或Exception子类继承的对象 因而自定义异常类应该作为Exception或其它标准异常类的子类 这样 假如在一个模块中引发了一个新定义的异常 而这个模块并没有包含对应的异常响应 则缺省异常处理机制将响应该异常 显示一个包含异常类名称和错误信息的消息框
下面是一个异常类的定义
type
EMyException = Class(Exception)
自引发异常
引发一个异常 调用保留字raise 后边跟一个异常类的实例
假如定义
type
EPasswordInvalid = Class(Exception)
则在程序中如下的语句将引发一个EPasswordInvalid异常
If Password <>CorrectPassword then
raise EPasswordInvalid Create( Incorrect Password entered )
异常产生时把System库单元中定义的变量ErrorAddr的值置为应用程序产生异常处的地址 在你的异常处理过程中可以引用ErrorAddr的值
在自己引发一个异常时 同样可以为ErrorAddr分配一个值
为异常分配一个错误地址需要使用保留字at 使用格式如下
raise EInstance at Address_Expession
自定义异常的应用举例
下面我们给出一个利用自定义异常编程的完整实例
两个标签框(Label Label )标示对应编辑框的功能 编辑框PassWord和InputEdit用于输入口令和数字 程序启动时Label InputEdit不可见 当在PassWord中输入正确的口令时 Label InputBox出现在屏幕上 此时Label PassWord隐藏
设计时 令Label InputEdit的Visible属性为False 通过设置PassWord的PassWordChar可以确定输入口令时回显在屏幕上的字符
自定义异常EInvalidPassWord和EInvalidInput分别用于表示输入的口令非法和数字非法 它们都是自定义异常EInValidation的子类 而EInValidation直接从Exception异常类派生
下面是三个异常类的定义
type
EInValidation = class(Exception)
public
ErrorCode: Integer
constructor Create(Const Msg: StringErrorNum: Integer)
end
EInvalidPassWord = class(EInValidation)
public
constructor Create
end
EInvalidInput = class(EInValidation)
public
constructor Create(ErrorNum: Integer)
end
EInValidation增加了一个公有成员ErrorCode来保存错误代码 错误代码的增加提供了很大的编程灵活性 对于异常类 可以根据错误代码提供不同的错误信息 对于使用者可以通过截取错误代码 在try…except模块之外来处理异常
从以上定义可以发现 EInvalidPassWord和EInvalidInput的构造函数参数表中没有表示错误信息的参数 事实上 它们保存在构造函数内部 下面是三个自定义异常类构造函数的实现代码
constructor EInValidation Create(Const Msg: StringErrorNum: Integer)
begin
inherited Create(Msg)
ErrorCode := ErrorNum
end
constructor EInValidPassWord Create
begin
inherited Create( Invalid Password Entered )
end
constructor EInValidInput Create(ErrorNum: Integer)
var
Msg: String
begin
case ErrorNum of
:
Msg := Can not convert String to Number
:
Msg := Number is out of Range
else
Msg := Input is Invalid
end
inherited Create(Msg ErrorNum)
end
对于EInvalidInput ErrorCode= 表示输入的不是纯数字序列 而ErrorCode= 表示输入数值越界
lishixinzhi/Article/program/Delphi/201311/25189
EIntOverFlow异常类在Integer Word Longint三种整数类型越界时引发 如
var
I : Integer
a b c : Word
begin
a :=
b :=
c :=
for I := to do
begin
c := a*b*c
end
end
引发一个EIntOverFlow异常
EIntOverFlow异常类只有在编译选择框Option|Project|Over_Flow_Check Option选中时才产生 当关闭溢出检查 则溢出后变量保留该类整数的最大范围值
整数类型的范围如下表
表 整数类型的范围
━━━━━━━━━━━━━━━━━━━━━━━━━━━
类型 范围 格式
───────────────────────────
Shortint 有符号 位
Integer 有符号 位
Longint 有符号 位
Byte 无符号 位
Word 无符号 位
━━━━━━━━━━━━━━━━━━━━━━━━━━━
浮点异常
浮点异常是在进行实数 *** 作时产生的 它们都从一个EMathError类派生 但与整数异常相同 程序运行中引发的总是它的子类EInvalidOp EZeroDivide EOverFlow EUnderFlow
表 浮点异常类及其引发原因
━━━━━━━━━━━━━━━━━━━━━━━━
异常类 引发原因
────────────────────────
EInvalidOp 处理器碰到一个未定义的指令
EZeroDivide 试图被零除
EOverFlow 浮点上溢
EUnderFlow 浮点下溢
━━━━━━━━━━━━━━━━━━━━━━━━
EInvalidOp最常见的引发原因是没有协处理器的机器遇到一个协处理器指令 由于在缺省情况下Delphi总是把浮点运算编译为协处理器指令 因而在 以下微机上常常会碰到这个错误 此时只需要在单元的接口部分设置全局编译指示{$N } 选择利用运行时间库进行浮点运算 问题就可以解决了
各种类型的浮点数(Real Single Double Extended)越界引起同样的溢出异常 这同整数异常类是不同的
类型匹配异常
类型匹配异常EInvalidCast当试图用As *** 作符把一个对象与另一类对象匹配失败后引发
类型转换异常
类型转换异常EConvertError当试图用转换函数把数据从一种形式转换为另一种形式时引发 特别是当把一个字符串转换为数值时引发 下面程序中的两条执行语句都将引发一个EConvertError异常
var
rl : Real
int: Integer
begin
rl := StrToFloat( $ )
int := StrToInt( )
end
要注意并不是所有的类型转换函数都会引发EConvertError异常 比如函数Val当它无法完成字符串到数值的转换时只把错误代码返回 利用这一点我们在( )节中实现了输入的类型和范围检查
硬件异常
硬件异常发生的情况有两种 或者是处理器检测到一个它不能处理的错误 或者是程序产生一个中断试图中止程序的执行 硬件异常不能编译进动态链接库(DLLs)中 而只能在标准的应用中使用
硬件异常都是EProcessor异常类的子类 但运行时间并不会引发一个EProcessor 异常
表 硬件异常类及其产生原因
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
异常类 引发原因
─────────────────────────────────
Efault 基本异常类 是其它异常类的父类
EGPFault 一般保护错 通常由一个未 初始化的指针或对象引起
EStackFault 非法访问处理器的栈段
EPageFault Windows内存管理器不能正确使用交换文件
EInvalidOpCode 处理器碰到一个未定义的指令 这通常意味着处理器
试图去 *** 作非法数据或未初始化的内存
EBreakPoint 应用程序产生一个断点中断
ESingleStep 应用程序产生一个单步中断
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
EFault EGPFault 往往意味着致命的错误 而EBreakPoint ESingleStep被Delphi IDE的内置调试器处理 事实上前边的五种硬件异常的响应和处理对开发者来说都是十分棘手的问题
lishixinzhi/Article/program/Delphi/201311/25192
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)