(delphi11最新学习资料) Object Pascal 学习笔记---第9章第2节(finally代码块)

9.2 finally 代码块

​ 还有第四个用于异常处理的关键字,我已经提到过,但到目前为止还没有使用过,那就是 finallyfinally块用于执行一些应始终执行的操作(通常是清理操作)。事实上,无论是否发生异常,finally 代码块中的语句都会被处理。只有在未出现异常或出现异常并已处理的情况下,才会执行 try 代码块后面的普通代码。换句话说,即使出现异常,finally 代码块中的代码也总是在 try 代码块的代码之后执行。

​ 请看这个方法(ExceptFinally 示例的一部分),它执行一些耗时的操作,并在窗体的标题中显示其状态:

procedure TForm1.BtnWrongClick(Sender: TObject);
var
  I, J: Integer;
begin
  Caption := 'Calculating';
  J := 0;
  // 长时间的(错误的)计算...
  for I := 1000 downto 0 do
    J := J + J div I;
  Caption := 'Finished';
  Show('Total: ' + J.ToString);
end;

​ 由于算法中存在错误(因为变量 I 的值可以达到 0,而且还用于除法),程序会中断,但不会重置窗体标题。这就是 try-finally 块的作用:

procedure TForm1.BtnTryFinallyClick(Sender: TObject);
var
  I, J: Integer;
begin
  Caption := 'Calculating';
  J := 0;
  try
    // 长时间的(错误的)计算...
    for I := 1000 downto 0 do
      J := J + J div I;
    Show('Total: ' + J.ToString);
  finally
    Caption := 'Finished';
  end;
end;

​ 当程序执行该函数时,无论是否出现(任何类型的)异常,都会重置游标。此版本函数的缺点是无法处理异常。

9.2.1 finallyexcept

​ 耐人寻味的是,在 Object Pascal 语言中,一个 try 代码块后面可以跟一个 Exceptfinally 语句,但不能同时跟这两个语句。鉴于您经常希望同时使用这两个块,典型的解决方案是使用两个嵌套的 try 块,内部块与 finally 语句相关联,外部块与 except 语句相关联,或者根据情况反之亦然。下面是 ExceptFinally 示例中第三个按钮的代码:

procedure TForm1.BtnTryTryClick(Sender: TObject);
var
  I, J: Integer;
begin
  Caption := 'Calculating';
  J := 0;
  try
    try
      // 长时间的(错误的)计算...
      for I := 1000 downto 0 do
        J := J + J div I;
      Show('Total: ' + J.ToString);
    except
      on E: EDivByZero do
      begin
        // 使用新消息重新引发异常
        raise Exception.Create('Error in Algorithm');
      end;
    end;
  finally
    Caption := 'Finished';
  end;
end;
9.2.2 使用 finally代码块恢复光标

try-finally 块的一个常见用例是资源的分配和释放。另一种相关情况是在操作完成后需要重置临时配置,即使该操作引发了异常。

​ 需要还原的临时配置设置的一个例子是沙漏光标,它在长时间操作中显示,并在操作结束后还原为原来的光标。即使代码很简单,也总有可能出现异常,因此应始终使用 try-finally 块。

​ 在 RestoreCursor 应用程序示例(一个 VCL 应用程序,因为 FireMonkey 中的光标管理比较复杂)中,我编写了以下代码,用于保存当前光标,将其临时设置为沙漏光标,并在操作结束时恢复原始光标:

var
  CurrCur := Screen.Cursor;
  Screen.Cursor := crHourGlass;
try
  // 一些耗时操作
  Sleep(5000);
finally
  Screen.Cursor := CurrCur;
end;
9.2.3 使用托管记录恢复光标

​ 要保护资源分配或定义要还原的临时配置,可以使用托管记录来代替显式的 try-finally 块,这需要编译器添加一个固有的 finally 块。这样,即使在定义记录时需要付出一些初始努力,保护资源或恢复配置的代码编写量也会减少。

​ 这是一个托管记录,代表了上一节代码的相同行为,即在 Initialize 方法中将当前游标保存在一个字段中,并在 Finalize 方法中将其重置:

type
  THourCursor = record
  private
    FCurrCur: TCursor;
  public
    class operator Initialize(out ADest: THourCursor);
    class operator Finalize(var ADest: THourCursor);
  end;

class operator THourCursor.Initialize(out ADest: THourCursor);
begin
  ADest.FCurrCur := Screen.Cursor;
  Screen.Cursor := crHourGlass;
end;

class operator THourCursor.Finalize(var ADest: THourCursor);
begin
  Screen.Cursor := ADest.FCurrCur;
end;

一旦定义了这个托管记录:

var
  HC: THourCursor;
// 一些耗时操作
Sleep(5000);

注解:你可以在 Erik van Bilsen 的以下博文中找到更多通过托管记录保护资源的例子: https://blog.grijjy.com/2020/08/03/automate-restorable-operations-with-custom-managed-records/。这是一系列关于托管记录的详细博客的一部分。

  • 5
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值