9.1 Try-Except块
让我从一个相当简单的 try-except 示例(ExceptionsTest 示例的一部分)开始,这个示例有一个通用的异常处理块:
function DividePlusOne(A, B: Integer): Integer;
begin
try
// 如果B等于0,则引发异常
Result := A div B;
Inc(Result);
except
Result := 0;
end;
// 更多代码
end;
注解:在 Delphi 调试器中运行程序时,如果遇到异常,即使有异常处理程序,调试器也会默认停止程序。当然,这通常是你想要的,因为你想知道异常发生的位置,并逐步查看处理程序的调用过程。如果你只想让程序在异常处理完毕后运行,看看用户会看到什么,可以使用 "不调试运行"命令运行程序,或者在调试器选项中禁用所有(或某种)异常。
像上面的代码那样 "沉默 "异常并将结果设置为零,在实际应用中并没有太大意义(因为向用户掩盖这样的错误通常是不好的做法),但这段代码的目的是帮助你理解简单场景中的核心机制。
这是用于调用该函数的事件处理程序的代码:
var
N: Integer;
begin
N := DividePlusOne(10, Random(3));
Show(N.ToString);
正如您所看到的,程序使用随机生成的值,因此当您点击按钮时,您可能处于有效状态(3 次中的 2 次),也可能处于无效状态。这样就有了两种不同的程序流程:
- 如果B不为零,则程序执行除法,执行递增,然后跳过
except
块,进入紧跟后面的end
语句(// 更多
)。 - 如果B为零,则除法引发异常,跳过所有后续语句(在这种情况下只有一个语句),直到第一个封闭的
try-except
块执行。在异常块之后,程序不会回到原始语句,而是跳过except
块, 执行其后的第一条语句(// 更多
)。
描述这种异常模型的一种方法是,它遵循一种非恢复的方法。如果出现错误,试图处理错误条件并返回到导致错误的语句是非常危险的,因为此时程序的状态很可能是未定义的。异常会极大地改变执行流程,跳过下面语句的执行并回滚堆栈,直到找到适当的错误处理代码。
上面的代码具有一个非常简单的except
块,没有on
语句。当您需要处理多种类型的异常(或多个异常类类型)或者要访问传递给代码块的异常对象,则需要一个或多个 on
语句:
function DividePlusOneBis(A, B: Integer): Integer;
begin
try
Result := A div B; // 如果B等于0则错误
Result := Result + 1;
except
on E: EDivByZero do
begin
Result := 0;
ShowMessage(E.Message);
end;
end;
end;
在异常处理语句中,我们捕获了运行时库定义的 EDivByZero 异常。有许多这样的异常类型,它们指的是运行时问题(如除以零或错误的动态转换)、系统问题(如内存不足错误)或组件错误(如索引无效)。所有这些异常类都继承自基类 Exception,后者提供了一些最基本的功能,如我在上面代码中使用的 Message 属性。这些类实际形成了一个具有一定逻辑的层次结构。
注解:Object Pascal 中的类型一般以字母 T 开头,而异常类则例外,一般以字母 E 开头。