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

发布于 2024-11-18 22:02:47 字数 1151 浏览 3 评论 0原文

以下面的代码为例:

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 some 部分发生错误,我认为创建的 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;

现在,除非我做了一些非常愚蠢的事情,否则没有理由在 Final 块中两次使用相同的代码以及之后,以及 Exception 块中。

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

我仍然在学习 Delphi,所以如果这看起来是一个简单的问题/答案,我深表歉意。

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

Take the following code as a sample:

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;

if there was an error happening in the // do something section, the TSomeObject that was created I assume will not be freed and the Screen.Cursor will still be stuck as an Hour Glass, because the code was broke before getting to those lines?

Now unless I am mistaking, an Exception statement should be in place to deal with any such occurence of an error, something like:

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;

Now unless I am doing something really stupid, there should be no reason to have the same code twice in the Finally block and after, and in the Exception block.

Basically I sometimes have some procedures that may be similar to the first sample I posted, and if I get an error the cursor is stuck as an Hour Glass. Adding the Exception handlers help, but it seems a dirty way of doing it - its basically ignoring the Finally block, not to mention ugly code with copy-paste from the Finally to Exception parts.

I am still very much learning Delphi so apologies if this appears to be a straight forward question/answer.

How should the code be correctly written to deal with the Statements and correctly freeing objects and capturing errors etc?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

毁虫ゝ 2024-11-25 22:02:47

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

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


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

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

You just need two try/finally blocks:

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

The guideline to follow is that you should use finally rather than except for protecting resources. As you have observed, if you attempt to do it with except then you are forced to write the finalising code twice.

Once you enter the try/finally block, the code in the finally section is guaranteed to run, no matter what happens between try and finally.

So, in the code above, the outer try/finally ensures that Screen.Cursor is restored in the face of any exceptions. Likewise the inner try/finally ensures that Obj is destroyed in case of any exceptions being raised during its lifetime.


If you want to handle an exception then you need a distinct try/except block. However, in most cases you should not attempt to handle exceptions. Just let it propagate up to the main application exception handler which will show a message to the user.

If you handle the exception to low down the call chain then the calling code will not know that the code it called has failed.

烟雨扶苏 2024-11-25 22:02:47

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

As others have explained, you need to protect the cursor change with try finally block. To avoid writing those I use code like this:

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.

Now you just use it like

uses
   autoCursor;

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

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

and Delphi's reference counted interface mechanism takes care of restoring the cursor.

滥情哥ㄟ 2024-11-25 22:02:47

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

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都会被执行当你//做某事时。即使发生异常(在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;

Your original code isn't quite as bad as you think (it's bad, though):

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 will be executed no matter what happens when you // do something. Even if an exception occurrs (after try), the finally block will be executed! That is the whole point of the try..finally construct!

But you also want to restore the cursor. The best way is to use two try..finally constructs:

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;
樱花坊 2024-11-25 22:02:47

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

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;

I think the most "correct" version would be this:

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;
不顾 2024-11-25 22:02:47

如果您在这里找到了方法,并且正在寻找如何在 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,尽管前者更常见。

If you found your way here and were looking for how you make a try-except-finally construct from C# in Delphi:

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

The answer is that you cannot do this directly. Instead, as @sacconago hints at, nest the try blocks as follows:

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

One nice feature of Delphi is that you can nest the blocks as try...except...finally or try...finally...except, though the former would be more common.

沫尐诺 2024-11-25 22:02:47

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

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;

注意最后的尝试;在 try except 内;以及 Obj 创建的位置。

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

Having done a lot of code in services/servers that needs to handle exceptions and not kill the app I usually go for something like this:

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;

Note the try finally; inside the try except; and the placement of Obj creation.

if the Obj creates other things inside it's constructor, it may work half-way and fail with an exception inside the .create(); but still be a created Obj. So I make sure that the Obj is always destroyed if it's been assigned...

故人爱我别走 2024-11-25 22:02:47

我会这样做:

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;

I would do it like this:

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;
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文