在 Delphi 中避免嵌套的 try...finally 块 [英] Avoiding nested try...finally blocks in Delphi

查看:19
本文介绍了在 Delphi 中避免嵌套的 try...finally 块的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

今天早上我有一个关于避免嵌套 try finally 块的想法,如下所示

I had this idea this morning on avoiding nested try finally blocks like the following

procedure DoSomething;
var
  T1, T2, T3 : TTestObject;
begin
  T1 := TTestObject.Create('One');
  try
    T2 := TTestObject.Create('Two');
    try
      T3 := TTestObject.Create('Three');
      try
        //A bunch of code;
      finally
        T3.Free;
      end;
    finally
      T2.Free;
    end;
  finally
    T1.Free;
  end;
end;

利用接口的自动引用计数,我想出了

By taking advantage of the automated reference counting of interfaces, I have come up with

Type  
  IDoFinally = interface
    procedure DoFree(O : TObject);
  end;

  TDoFinally = class(TInterfacedObject, IDoFinally)
  private
    FreeObjectList : TObjectList;
  public
    procedure DoFree(O : TObject);
    constructor Create;
    destructor Destroy; override;
  end;

//...

procedure TDoFinally.DoFree(O : TObject);
begin
  FreeObjectList.Add(O);
end;

constructor TDoFinally.Create;
begin
  FreeObjectList := TObjectList.Create(True);
end;

destructor TDoFinally.Destroy;
begin
  FreeObjectList.Free;
  inherited;
end;

使前面的代码块变成

procedure DoSomething;
var
  T1, T2, T3 : TTestObject;
  DoFinally : IDoFinally;
begin
  DoFinally := TDoFinally.Create;
  T1 := TTestObject.Create('One');
  DoFinally.DoFree(T1);
  T2 := TTestObject.Create('Two');
  DoFinally.DoFree(T2);
  T3 := TTestObject.Create('Three');
  DoFinally.DoFree(T3);
  // A Bunch of code;
end;

我的问题是:这行得通还是我忽略了什么?

My question is: does this work or have I overlooked something?

对我来说这看起来很酷,并且通过减少嵌套使代码更易于阅读.它还可以扩展为存储匿名方法列表以运行以执行诸如关闭文件、查询等操作...

To me this looks pretty cool, and makes the code a bit easier to read with the reduced amount of nesting. It could also be extended to store a list of anonymous methods to run to do things such as close files, queries, etc...

推荐答案

是的,它有效.

也许原始代码的嵌套 try-finally 块与使用引用计数对象管理其他对象的生命周期的技术之间唯一不同的是,如果销毁任何对象出现问题,会发生什么.如果在销毁任何对象时出现异常,嵌套的 try-finally 块将确保任何剩余的对象仍将被释放.TDoFinally 中的 TObjectList 不会这样做;如果列表中的任何项目无法销毁,则列表中的任何后续项目都将泄漏.

Perhaps the only thing different between the nested try-finally blocks of the original code and the technique of using a reference-counted object to manage the lifetimes of other objects is what happens if there's a problem destroying any of the objects. If there's an exception while any object is being destroyed, the nested try-finally blocks will ensure that any remaining objects will still get freed. The TObjectList in your TDoFinally doesn't do that; if any item in the list can't be destroyed, any subsequent items in the list will be leaked.

实际上,这并不是真正的问题.任何析构函数都不应该抛出异常.如果是这样,无论如何都没有真正的方法可以从中恢复,因此是否因此而泄漏也没有关系.无论如何,您的程序都应该立即终止,因此拥有一个整洁的清理例程并不重要.

In practice, that's not really a problem, though. No destructor should ever throw an exception. If it does, there's not really any way to recover from it anyway, so it doesn't matter if anything leaks because of it. Your program should terminate momentarily anyway, so having a tidy cleanup routine is of little importance.

顺便说一下,JCL 已经提供了 ISafeGuardIMultiSafeGuard> 用于管理本地对象生命周期的接口.例如,您可以像这样重写代码:

Incidentally, the JCL already offers the ISafeGuard and IMultiSafeGuard interfaces for managing local objects' lifetimes. For example, you could rewrite your code like this:

uses JclSysUtils;

procedure DoSomething;
var
  T1, T2, T3: TTestObject;
  G: IMultiSafeGuard;
begin
  T1 := TTestObject(Guard(TTestObject.Create('One'), G));
  T2 := TTestObject(Guard(TTestObject.Create('Two'), G));
  T3 := TTestObject(Guard(TTestObject.Create('Three'), G));
  // A Bunch of code;
end;

那个库也没有解决析构函数中的异常.

That library doesn't address exceptions in destructors, either.

这篇关于在 Delphi 中避免嵌套的 try...finally 块的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆