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

时间:2011-07-06 18:23:04

标签: 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 something部分中发生错误,我创建的TSomeObject将不会被释放,而Screen.Cursor仍然会被作为小时玻璃卡住,因为代码在获取之前已经破了那些线?

现在除非我误会,否则应该有一个Exception语句来处理任何错误的发生,例如:

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块和之后以及在Exception块中两次使用相同的代码。

基本上我有时会有一些程序可能与我发布的第一个样本类似,如果我收到错误,光标会被卡在小时玻璃上。添加Exception处理程序会有所帮助,但这似乎是一种肮脏的方式 - 它基本上忽略了Finally块,更不用说带有从Last到Exception部分的copy-paste的丑陋代码。

如果这似乎是一个直截了当的问题/答案,我仍然非常了解Delphi,所以道歉。

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

7 个答案:

答案 0 :(得分:31)

您只需要两个try/finally块:

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

要遵循的准则是,您应使用finally而不是except来保护资源。正如您所观察到的,如果您尝试使用except,那么您将被迫两次编写最终代码。

一旦您输入try/finally块,无论finallytry之间发生什么,finally部分中的代码都会保证运行。

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


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

如果你处理异常以降低调用链,那么调用代码就不会知道它调用的代码失败了。

答案 1 :(得分:17)

您的原始代码没有您想象的那么糟糕:

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;
无论Obj.Free发生什么,

// do something 都将执行。即使发生异常(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;

[但是,我也不介意

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

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

end;
太多了。 Screen.Cursor := crHourGlass失败的风险非常低,但在这种情况下,对象将不会被释放(finally将无法运行,因为您不在try内,所以双try..finally更安全。]

答案 2 :(得分:14)

正如其他人所解释的那样,您需要使用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.

现在你就像使用它一样

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的引用计数接口机制负责恢复游标。

答案 3 :(得分:3)

我会这样做:

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;

答案 4 :(得分:1)

在需要处理异常而不是杀死应用程序的服务/服务器中完成了很多代码我通常会这样做:

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;

请注意最后的尝试;里面的尝试除外;以及Obj创作的位置。

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

答案 5 :(得分:0)

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

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;

答案 6 :(得分:0)

如果您在这里找到了自己的方式并正在寻找如何在 Delphi 中从 C# 构建 try-except-finally 构造:

// C#
try
{
    // Do something
}
catch
{
    // Exception!
}
finally
{
    // Always do this...
}

答案是您不能直接执行此操作。相反,正如@sacconago 所暗示的,嵌套 try 块如下:

// Delphi
try
    try
        // Do something
    except
        // Exception!
    end;
finally
    // Always do this...
end;

Delphi 的一个很好的功能是您可以将块嵌套为 try...except...finallytry...finally...except,尽管前者更为常见。