• DELPHI学习(异常)


    Exceptions(异常)
    Exceptions: Overview(概述)
    当发生错误或其它事件而打断了程序的正常执行时,将引发一个异常。异常把控制权交给一个异常处理
    程序(exception handler),这使我们把错误处理和正常的程序逻辑隔离开来。因为异常属于对象,我们
    可以应用继承关系把它们分层组织,在不影响现有代码的情况下能引入新的异常。异常能传送一些信息
    (比如错误消息),把它们从异常发生点带到被处理的地方。
    当程序使用SysUtils 单元时,所有的运行时错误都将被转换为异常,否则,像内存不足、被零除、GPF
    (general protection fault)等错误会终止程序,而现在它们能被捕获并进行处理。


    When to use exceptions(何时使用异常)
    异常提供了一种优雅的方式来捕获运行时错误,而不是挂起程序和使用笨拙的条件语句。但是,Object
    Pascal 异常处理机制的复杂性降低了它的效率,所以应当酌情使用。虽然(几乎)能以任何原因引发一
    个异常,也可以把(几乎)任何代码段使用try...except 或try...finally 封装起来进行保护,但实际上最好
    把它们用在特殊情况。
    异常处理程序适用于以下几种情况:发生几率比较低或难以预料、但结果却是灾难性(比如程序崩溃)
    的错误;对于if...then 语句来说,错误条件非常复杂或难以判断;当需要响应操作系统引发的异常,或
    一些你不能得到源码而又必须对它们的异常做出响应的例程。异常通常用在硬件、内存、I/O 和操作系
    统错误。
    条件语句经常是判断错误的最好方式。比如,假设你要在打开一个文件之前先确定它是否存在,你以下
    面的方式实现它:
    try
    AssignFile(F, FileName);
    Reset(F); // 若没有发现文件则引发一个EinOutError 异常
    except
    on Exception do ...
    end;
    但你也可以使用下面的方式来避免异常处理的开销
    if FileExists(FileName) then // 若没有发现文件则返回False,不会引发异常
    begin
    AssignFile(F, FileName);
    Reset(F);
    end;
    Assertions 提供了另一种方式,使你在源代码的任何地方判断一个布尔条件。当一个Assert 语句失败时,
    程序或者挂起,或者引发一个EAssertionFailed 异常(若它使用SysUtils 单元的话)。只有当判断一个你
    不期望发生的条件时,你才应该使用Assertions。要了解更多信息,请参考在线帮助中的the standard
    procedure Assert。


    Declaring exception types(声明异常类型)
    异常类的声明和其它类一样,实际上,使用任何类的一个实例表示异常是可行的,但推荐从SysUtils 单
    元的Exception 类进行派生。
    你能应用继承关系给异常分组,比如,下面是SysUtils 单元中的声明,它为计算错误定义了一组异常类
    type
    EMathError = class(Exception);
    EInvalidOp = class(EMathError);
    EZeroDivide = class(EMathError);
    EOverflow = class(EMathError);
    EUnderflow = class(EMathError);
    给定上面的声明,你能定义一个单一的EMathError 异常处理程序,它也能处理EInvalidOp、EZeroDivide、
    EOverflow 和EUnderflow 异常。
    有时,异常类会定义字段、方法和属性,它们用来传达一些额外的错误信息。比如,
    type EInOutError = class(Exception)
    ErrorCode: Integer;
    end;
    Raising and handling exceptions(引发和处理异常)
    Raising and handling exceptions(引发和处理异常)
    要创建一个异常对象,在raise 语句中调用异常类的构造函数。比如,
    raise EMathError.Create;
    通常,raise 语句的格式是
    raise object at address
    这里,object 和at address 都是可选的。若省略了object,则语句重新引发当前异常,请参考Re-raising
    exceptions;当指定了一个地址,它通常是指向过程或函数的指针,使用这个选项,可使异常从堆栈中一
    个较早点引发,而不是从它实际发生的地点引发(use this option to raise the exception from an earlier point
    in the stack than the one where the error actually occurred)。
    当引发一个异常时,也就是使用了raise 语句(referenced in a raise statement),它将受异常处理逻辑的控
    制。一个raise 语句永远不会以正常方式返回控制,相反,它把控制权交给能处理指定的异常(类)、并
    且在最内层的异常处理程序(最内层是指最后进入但还没有退出的一个try...except 块)。
    比如,下面的函数把一个字符串转换为整数,若结果超出指定的范围则引发一个ERangeError 异常。
    function StrToIntRange(const S: string; Min, Max: Longint): Longint;
    begin
    Result := StrToInt(S); // StrToInt 在SysUtils 单元声明
    if (Result < Min) or (Result > Max) then
    raise ERangeError.CreateFmt(
    '%d is not within the valid range of %d..%d',
    [Result, Min, Max]);
    end;
    注意raise 语句中调用的CreateFmt 方法。Exception 和它的派生类有特殊的构造函数,提供了可选择的
    方法来创建异常消息和context ID。
    被引发的(raised)异常在处理后自动清除,永远不要试图手动销毁它。
    注意:在单元的初始化部分引发一个异常可能无法产生预期的结果。对异常的正规支持来自SysUtils 单
    元,在获得这种支持之前,它必须被初始化。如果在初始化期间产生了异常,所有被初始化的单元(包
    括SysUtils)执行结束化处理,并重新引发异常。然后,通常是结束程序来捕获和处理异常(Then the
    exception is caught and handled, usually by interrupting the program)。
    Try … except statements(Try … except 语句)
    异常在try...except 语句中被处理,比如,
    try
    X := Y/Z;
    except
    on EZeroDivide do HandleZeroDivide;
    end;
    上面的语句尝试Y 被Z 除,若EZeroDivide 异常发生,则调用例程HandleZeroDivide。
    try...except 语句的语法是
    try statements except exceptionBlock end
    这里,statements 是语句序列(由分号隔开的一系列语句),exceptionBlock 或者是
    • 其它语句序列,或者是
    • 一系列异常处理程序,后面跟可选的
    else statements
    一个异常处理程序具有如下格式
    on identifier: type do statement
    这里,identifier:是可选的(若有的话,它可以是任何有效标志符),type 用来表示异常类,statement 是任
    何语句。
    一个try...except 语句执行开始处的(初始)代码,若没有引发异常,异常代码段(exceptionBlock)被忽
    略,程序控制转到下一部分。
    若执行初始代码时发生了异常(或者执行了raise 语句,或者是调用过程或函数引起的),都将试图对它
    进行处理:
    • 若异常处理块(exception block)中有对应的异常,则控制权交给第一个匹配的处理程序。当处理程
    序中指定的异常类和(发生的)异常所属的类相同,或者是异常的祖先类时,我们说,这个异常处
    理程序与这个异常相“匹配”。
    • 若没有发现相应的异常处理程序,当有else 子句时,程序控制转到else 子句。
    • 若异常处理块中没有异常处理程序,而只是语句序列,则程序控制转到它的第一个语句。
    如果上面的条件都不成立,会继续搜索下一个try...except 语句块;若还没有合适的异常处理程序、或else
    子句或语句序列,搜索会继续扩展到下一个try...except 语句块,依此类推。如果达到最外层的try...except
    语句块并且异常还没有被处理,程序就会终止。
    当处理一个异常时,堆栈退回到包含try...except 语句的过程或函数,程序控制权转给异常处理程序、else
    子句或语句序列。这个过程忽略所有进入try...except 后调用的过程和函数,然后,异常对象自动调用析
    构函数进行销毁,程序控制权转给try...except 后面的语句。(如果调用Exit、Break 或Continue 使程序控
    制权离开了异常处理程序,异常对象也会自动销毁。)
    在下面的例子中,第1 个异常处理程序处理被0 除异常,第2 个处理溢出,最后一个处理其它的数学运
    算异常。EMathError 在最后出现,因为它是另外两个异常的祖先,若它最先出现,另外两个将永远不会
    被调用。
    try
    ...
    except
    on EZeroDivide do HandleZeroDivide;
    on EOverflow do HandleOverflow;
    on EMathError do HandleMathError;
    end;
    在异常处理程序中,可以在异常类之前指定一个标志符,在执行on...do 后面的语句时,它表示异常对象,
    标志符的作用域被限定在这个语句中。比如,
    try
    ...
    except
    on E: Exception do ErrorDialog(E.Message, E.HelpContext);
    end;
    若在异常处理块中使用了else 子句,则它处理所有未经异常处理程序处理的异常。比如,
    try
    ...
    except
    on EZeroDivide do HandleZeroDivide;
    on EOverflow do HandleOverflow;
    on EMathError do HandleMathError;
    else
    HandleAllOthers;
    end;
    这里,else 子句处理所有不是EMathError 的异常。
    若异常处理块没有异常处理程序,而只是包含一系列语句,则它们处理所有的异常。比如,
    try
    ...
    except
    HandleException;
    end;
    这里,try 和except 之间的代码在运行时产生的异常,都由HandleException 例程进行处理。
    Re-raising exceptions(重新引发一个异常)
    当关键字raise 在异常块中出现,并且它的后面没有对象引用时,它引发正在处理的异常。这使得异常
    处理程序能对错误做有限处理后重新引发它。对于发生异常后必须进行清除工作、但又不能进行全面处
    理的过程或函数,重新引发一个异常是有用的。
    比如,GetFileList 函数分配一个TStringList 对象,并用指定搜索路径下的文件名来填充它。
    function GetFileList(const Path: string): TStringList;
    var
    I: Integer;
    SearchRec: TSearchRec;
    begin
    Result := TStringList.Create;
    try
    I := FindFirst(Path, 0, SearchRec);
    while I = 0 do
    begin
    Result.Add(SearchRec.Name);
    I := FindNext(SearchRec);
    end;
    except
    Result.Free;
    raise;
    end;
    end;
    GetFileList 创建一个TStringList 对象,然后使用FindFirst 和FindNext 函数(在SysUtils 单元定义)来初
    始化它。如果初始化失败(比如搜索路径无效,或者没有足够的内存来填充字符串列表),GetFileList
    需要释放字符串列表,因为函数的调用者还不知道它的存在。由于这个原因,初始化字符串列表在
    try...except 语句中执行,若发生了异常,异常处理块释放字符串列表,然后重新引发这个异常。
    Nested exceptions(嵌套的异常)
    对于异常处理程序,它自己也可以引发和处理异常。只要这些异常也是在异常处理程序的内部被处理,
    它们并不影响原来的异常;但是,若它超越了异常处理程序,原来的异常就会丢失。下面的Tan 函数说
    明了这一点。
    type
    ETrigError = class(EMathError);
    function Tan(X: Extended): Extended;
    begin
    try
    Result := Sin(X) / Cos(X);
    except
    on EMathError do
    raise ETrigError.Create('Invalid argument to Tan');
    end;
    end;
    若Tan 在执行过程中发生了EMathError 异常,则异常处理程序引发一个ETrigError 异常。因为Tan 没有
    为ETrigError 提异常供处理程序,异常就传播到原异常处理程序的外面,从而导致EMathError 被销毁。
    对于函数调用者来说,就像Tan 函数引发了一个ETrigError 异常。(不明白)
    Try … finally statements(Try … finally 语句)
    有时,我们希望不管有没有发生异常,指定的一部分操作都要被完全执行。比如,当一个例程需要控制
    一个资源,不管例程是否正常结束,能释放资源是非常重要的。在这种情况下,你可以使用try...finally
    语句。
    下面的例子演示这段代码如何打开和处理一个文件,并且,即使在执行过程中发生了错误也能保证在最
    后关闭文件。
    Reset(F);
    try
    ... // 处理文件F
    finally
    CloseFile(F);
    end;
    Classes and objects
    - 121 -
    try...finally 语句的语法是
    try statementList1 finally statementList2 end
    这里,每个statementList 是一系列由分号隔开的语句。try...finally 语句执行statementList1(try 子句)中
    的命令,若它执行完毕并没有引发异常,statementList2(finally 子句)被执行。若在执行statementList1
    时发生了异常,程序控制权转给statementList2,一旦它执行完毕,异常被重新引发。即使调用Exit、Break
    或Continue 过程使程序控制权离开了statementList1,statementList2 也会自动执行。所以,不论try 子句
    如何结束,finally 子句总是被执行。
    若异常发生了但却没有在finally 子句中进行处理,异常会传播到try...finally 语句的外面,这样,在try
    子句中已经引发的异常都会丢失。所以,finally 子句应当处理所有本地引发的异常,这样就不会打乱其
    它异常的传播。
    Standard exception classes and routines(标准异常类和例程)
    SysUtils 单元声明了几个标准例程来处理异常,它们包括ExceptObject、ExceptAddr 以及ShowException。
    SysUtils 和其它单元还包括很多异常类,它们(除了OutlineError)都是从Exception 派生而来。
    Exception 类有Message 和HelpContext 的属性,它们用于传递错误描述和context ID,后者用于上下文相
    关联机文档;它还定义了多个构造函数,使你能以不同的方式指定描述信息和context ID。

  • 相关阅读:
    WPF常用TriggerAction用法 (一)
    一个WPF只能输入数字的行为。
    自定义panel实现,并实现item更改和移除动画。
    MVVM模式下弹出窗体
    ZAM 3D 制作简单的3D字幕 流程(二)
    ZAM 3D 制作简单的3D字幕 流程(一)
    ZAM 3D 制作3D动画字幕 用于Xaml导出
    Metro Win8风格的按钮(Filp翻转)
    WPF自动隐藏的消息框(鼠标放上去将一直显示,移开动画继续),提供normal和error两种边框。
    可分组的选择框控件(MVVM下)(Toggle样式 仿造单选框RadioButton,复选框CheckBox功能)
  • 原文地址:https://www.cnblogs.com/moon25/p/1267766.html
Copyright © 2020-2023  润新知