28

以以下代码为例:

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 仍将作为沙漏卡住,因为代码在到达这些行之前就被破坏了?

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

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 块中使用相同的代码两次。

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

如果这似乎是一个直截了当的问题/答案,我仍然在学习 Delphi,所以很抱歉。

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

4

7 回答 7

38

你只需要两个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块,该finally部分中的代码保证运行,无论try和之间发生什么finally

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


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

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

于 2011-07-06T18:25:05.213 回答
18

您的原始代码并不您想象的那么糟糕(虽然很糟糕):

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;
于 2011-07-06T18:26:44.367 回答
16

正如其他人所解释的,您需要使用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 的引用计数接口机制负责恢复光标。

于 2011-07-06T19:03:29.030 回答
6

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

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;
于 2011-07-07T09:13:04.393 回答
2

我会这样做:

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;
于 2014-08-06T06:21:54.997 回答
2

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

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 被分配,它总是被销毁......

于 2011-07-07T10:27:59.543 回答
1

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

// 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,尽管前者更常见。

于 2021-07-18T22:49:32.547 回答