Delphi 如何正确编写Try..Finally..Except语句?

Delphi 如何正确编写Try..Finally..Except语句?,delphi,try-finally,try-except,Delphi,Try Finally,Try Except,以以下代码为例: procedure TForm1.Button1Click(Sender: TObject); var Obj: TSomeObject; begin Screen.Cursor:= crHourGlass; Obj:= TSomeObject.Create; try // do something finally Obj.Free; end; Screen.Cursor:= crDefault; end; 如果在//do so

以以下代码为例:

procedure TForm1.Button1Click(Sender: TObject);
var
  Obj: TSomeObject;
begin
  Screen.Cursor:= crHourGlass;

  Obj:= TSomeObject.Create;
  try
    // do something
  finally
    Obj.Free;
  end;

  Screen.Cursor:= crDefault;
end;
如果在
//do something
部分发生错误,我认为创建的TSomeObject将不会被释放,屏幕上的光标仍将被卡住,因为代码在到达这些行之前就被破坏了

现在,除非我弄错了,否则应该有一个异常语句来处理任何此类错误的发生,比如:

procedure TForm1.Button1Click(Sender: TObject);
var
  Obj: TSomeObject;
begin
  try
    Screen.Cursor:= crHourGlass;

    Obj:= TSomeObject.Create;
    try
      // do something
    finally
      Obj.Free;
    end;

    Screen.Cursor:= crDefault;
  except on E: Exception do
  begin
    Obj.Free;
    Screen.Cursor:= crDefault;
    ShowMessage('There was an error: ' + E.Message);
  end;
end;
现在,除非我正在做一些非常愚蠢的事情,否则应该没有理由在Finally块、after块和Exception块中使用相同的代码两次

基本上,我有时会有一些程序,可能类似于我发布的第一个示例,如果我出现错误,光标会被卡住。添加异常处理程序会有所帮助,但这似乎是一种肮脏的做法——它基本上忽略了Finally块,更不用说从Finally部分复制粘贴到异常部分的丑陋代码了

我仍在学习德尔福,如果这是一个直截了当的问题/答案,我深表歉意


如何正确编写代码来处理语句、正确释放对象和捕获错误等?

您只需要两个
try/finally
块:

Screen.Cursor:= crHourGlass;
try
  Obj:= TSomeObject.Create;
  try
    // do something
  finally
    Obj.Free;
  end;
finally
  Screen.Cursor:= crDefault;
end;
遵循的指导原则是,您应该使用
finally
而不是
except
来保护资源。正如您所观察到的,如果您尝试使用除之外的
执行此操作,那么您将被迫编写两次最终代码

进入
try/finally
块后,无论
try
finally
之间发生什么情况,
finally
部分中的代码都保证运行

因此,在上面的代码中,外部的
try/finally
确保
Screen.Cursor
在遇到任何异常时都会恢复。同样,内部
try/finally
确保在其生存期内出现异常时销毁
Obj


如果要处理异常,则需要一个不同的
try/except
块。但是,在大多数情况下,您不应该尝试处理异常。只需让它传播到主应用程序异常处理程序,它将向用户显示一条消息

如果您处理异常以降低调用链,则调用代码将不知道它调用的代码已失败。

您的原始代码并不像您想象的那么糟糕(尽管很糟糕):

Obj.Free
将被执行,无论当您
//做某事时发生什么。即使发生异常(在
try
之后),也将执行
finally
块!这就是
try..finally
构造的全部要点

但您也希望恢复光标。最好的方法是使用两种
try..finally
结构:

procedure TForm1.Button1Click(Sender: TObject);
var
  Obj: TSomeObject;
begin

  Screen.Cursor := crHourGlass;
  try
    Obj := TSomeObject.Create;
    try
      // do something
    finally
      Obj.Free;
    end;
  finally
    Screen.Cursor := crDefault;
  end;

end;

正如其他人所解释的,您需要使用
try finally
块来保护光标更改。为了避免编写这些代码,我使用如下代码:

unit autoCursor;

interface

uses Controls;

type
  ICursor = interface(IInterface)
  ['{F5B4EB9C-6B74-42A3-B3DC-5068CCCBDA7A}']
  end;

function __SetCursor(const aCursor: TCursor): ICursor;

implementation

uses Forms;

type
  TAutoCursor = class(TInterfacedObject, ICursor)
  private
    FCursor: TCursor;
  public
    constructor Create(const aCursor: TCursor);
    destructor Destroy; override;
  end;

{ TAutoCursor }
constructor TAutoCursor.Create(const aCursor: TCursor);
begin
  inherited Create;
  FCursor := Screen.Cursor;
  Screen.Cursor := aCursor;
end;

destructor TAutoCursor.Destroy;
begin
  Screen.Cursor := FCursor;
  inherited;
end;

function __SetCursor(const aCursor: TCursor): ICursor;
begin
  Result := TAutoCursor.Create(aCursor);
end;

end.
procedure TForm1.Button1Click(Sender: TObject);
var
   Obj: TSomeObject;
begin  
     try
        Obj := NIL;
        try
          Screen.Cursor := crHourGlass;
          Obj := TSomeObject.Create;
          // do something
        finally
          Screen.Cursor := crDefault;
          if assigned(Obj) then FreeAndNil(Obj);
        end;
     except
        On E: Exception do ; // Log the exception
     end;
end;
var
  savedCursor: TCursor;
  Obj: TSomeObject;
begin
  savedCursor := Screen.Cursor;
  Screen.Cursor := crHourGlass;
  Obj:= TSomeObject.Create;
  try
    try
      // do something
    except
      // record the exception
    end;
  finally
    if Assigned(Obj) then
      Obj.Free;
    Screen.Cursor := savedCursor;
  end;
end;
现在你就这样使用它

uses
   autoCursor;

procedure TForm1.Button1Click(Sender: TObject);
var
  Obj: TSomeObject;
begin
  __SetCursor(crHourGlass);

  Obj:= TSomeObject.Create;
  try
    // do something
  finally
    Obj.Free;
  end;
end;
Delphi的引用计数接口机制负责恢复光标。

我认为最“正确”的版本应该是:

procedure TForm1.Button1Click(Sender: TObject);
var
  Obj: TSomeObject;
begin
  Obj := NIL;
  Screen.Cursor := crHourGlass;
  try
    Obj := TSomeObject.Create;
    // do something
  finally
    Screen.Cursor := crDefault;
    Obj.Free;
  end;
end;

在服务/服务器中编写了大量代码,需要处理异常,而不是杀死应用程序后,我通常会选择以下方式:

unit autoCursor;

interface

uses Controls;

type
  ICursor = interface(IInterface)
  ['{F5B4EB9C-6B74-42A3-B3DC-5068CCCBDA7A}']
  end;

function __SetCursor(const aCursor: TCursor): ICursor;

implementation

uses Forms;

type
  TAutoCursor = class(TInterfacedObject, ICursor)
  private
    FCursor: TCursor;
  public
    constructor Create(const aCursor: TCursor);
    destructor Destroy; override;
  end;

{ TAutoCursor }
constructor TAutoCursor.Create(const aCursor: TCursor);
begin
  inherited Create;
  FCursor := Screen.Cursor;
  Screen.Cursor := aCursor;
end;

destructor TAutoCursor.Destroy;
begin
  Screen.Cursor := FCursor;
  inherited;
end;

function __SetCursor(const aCursor: TCursor): ICursor;
begin
  Result := TAutoCursor.Create(aCursor);
end;

end.
procedure TForm1.Button1Click(Sender: TObject);
var
   Obj: TSomeObject;
begin  
     try
        Obj := NIL;
        try
          Screen.Cursor := crHourGlass;
          Obj := TSomeObject.Create;
          // do something
        finally
          Screen.Cursor := crDefault;
          if assigned(Obj) then FreeAndNil(Obj);
        end;
     except
        On E: Exception do ; // Log the exception
     end;
end;
var
  savedCursor: TCursor;
  Obj: TSomeObject;
begin
  savedCursor := Screen.Cursor;
  Screen.Cursor := crHourGlass;
  Obj:= TSomeObject.Create;
  try
    try
      // do something
    except
      // record the exception
    end;
  finally
    if Assigned(Obj) then
      Obj.Free;
    Screen.Cursor := savedCursor;
  end;
end;
注意最后的尝试;内试除外;以及Obj创建的位置


如果Obj在其构造函数中创建了其他内容,它可能会半途而废,并在.create()中出现异常而失败;但仍然是一个创造的对象。因此,如果Obj被分配,我会确保它总是被销毁…

我会这样做:

unit autoCursor;

interface

uses Controls;

type
  ICursor = interface(IInterface)
  ['{F5B4EB9C-6B74-42A3-B3DC-5068CCCBDA7A}']
  end;

function __SetCursor(const aCursor: TCursor): ICursor;

implementation

uses Forms;

type
  TAutoCursor = class(TInterfacedObject, ICursor)
  private
    FCursor: TCursor;
  public
    constructor Create(const aCursor: TCursor);
    destructor Destroy; override;
  end;

{ TAutoCursor }
constructor TAutoCursor.Create(const aCursor: TCursor);
begin
  inherited Create;
  FCursor := Screen.Cursor;
  Screen.Cursor := aCursor;
end;

destructor TAutoCursor.Destroy;
begin
  Screen.Cursor := FCursor;
  inherited;
end;

function __SetCursor(const aCursor: TCursor): ICursor;
begin
  Result := TAutoCursor.Create(aCursor);
end;

end.
procedure TForm1.Button1Click(Sender: TObject);
var
   Obj: TSomeObject;
begin  
     try
        Obj := NIL;
        try
          Screen.Cursor := crHourGlass;
          Obj := TSomeObject.Create;
          // do something
        finally
          Screen.Cursor := crDefault;
          if assigned(Obj) then FreeAndNil(Obj);
        end;
     except
        On E: Exception do ; // Log the exception
     end;
end;
var
  savedCursor: TCursor;
  Obj: TSomeObject;
begin
  savedCursor := Screen.Cursor;
  Screen.Cursor := crHourGlass;
  Obj:= TSomeObject.Create;
  try
    try
      // do something
    except
      // record the exception
    end;
  finally
    if Assigned(Obj) then
      Obj.Free;
    Screen.Cursor := savedCursor;
  end;
end;

@David这就是我的问题,当发生错误时,光标会被卡住一个小时glass@Andreas我要引用你的话。“你永远不知道。这就像系安全带一样。大多数时候,这显然是不必要的,但这是值得的,因为总有发生事故的风险。”我一直在用这个来“恢复”光标到正常状态+1没有必要再尝试了。最后,安德烈亚斯。只需在finally中反转顺序-Obj将始终被释放,可能发生的所有糟糕情况是光标无法更改。@Fabricio:我想我们也应该在
尝试之前反转顺序。但是如果光标停留在沙漏上,这是一个恼人的错误,不是吗?写一个嵌套的
try..finally
不是很多工作…请解释一下为什么在这段代码中添加try/finally块?安德烈亚斯推断问题背后是对try/finally含义的理解,这可能是正确的。我不确定你的意思,但我补充了try.最后是为了确保someobject一旦完成就从记忆中释放出来,需要做的每一件事都很聪明,也许太聪明了。我不会提倡这一点,尤其是对那些相对较新的人来说。如果你一定要这么做,为什么还要费心于伊库索呢。让SetCursor返回界面就行了。为什么不呢,那里没有危险。编译器只是为您创建一个不可见的
try finally
块。。。ICursor提供了额外的功能(不包括在示例中),即读取要还原的原始游标的属性。哦,它工作得非常好。这只是复杂性的另一个层次。非常聪明,但Delphi只是在幕后添加了一个不可见的
try finally
,以确保引用计数为零,因此您只需增加复杂性,以确保几次击键的安全,同时强制编译器生成与您自己额外尝试finally基本相同的代码。我没有看到这里的好处,它看起来像是一场代码混淆比赛。在我看来,显式try finally要好得多,因为它更好地解释了代码的意图,并且代码是为人类而编写的,而不是为机器而编写的