web-dev-qa-db-ja.com

Try..Finally..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ブロックで同じコードを2回持つ理由はないはずです。

基本的に、最初に投稿したサンプルに似た手順がいくつかあり、エラーが発生した場合はカーソルが砂時計としてスタックしています。例外ハンドラーを追加することは役立ちますが、それを行うのは汚い方法のようです-基本的に、Finallyブロックを無視します。

私はまだDelphiを非常に学んでいるので、これが簡単な質問/回答であると思われる場合は謝罪します。

ステートメントを処理し、オブジェクトを正しく解放し、エラーをキャプチャするために、どのようにコードを正しく記述する必要がありますか?

24
user741875

必要なのは2つのtry/finallyブロック:

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

従うべきガイドラインは、リソースを保護するためにfinallyではなくexceptを使用することです。あなたが観察したように、exceptでそれを行おうとすると、ファイナライズコードを2回書くことを余儀なくされます。

try/finallyブロックでは、finallytryの間で何が起こっても、finallyセクションのコードの実行が保証されます。

したがって、上記のコードでは、外側のtry/finallyScreen.Cursorは、例外が発生した場合に復元されます。同様に、内側のtry/finallyは、そのライフタイム中に例外が発生した場合にObjが確実に破棄されるようにします。


例外を処理する場合は、_try/exceptブロック。ただし、ほとんどの場合、not例外の処理を試みる必要があります。ユーザーにメッセージを表示するメインアプリケーション例外ハンドラーまで伝播させるだけです。

例外を処理して呼び出しチェーンを低くすると、呼び出し元のコードは、呼び出したコードが失敗したことを知りません。

33
David Heffernan

元のコードは思ったほど悪くはありません:

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.Freewill// do somethingのときに何が起きても実行されます。例外が発生した場合(tryの後)、finallyブロックwillが実行されます!これがtry..finallyコンストラクトのポイントです!

ただし、カーソルも復元する必要があります。最も用心深い方法は、2つの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の方が安全です。]

17

他の人が説明したように、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の参照カウントインターフェイスメカニズムは、カーソルの復元を処理します。

14
ain

私はこのようにします:

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;
3
sacconago

例外を処理し、アプリを殺さないようにする必要があるサービス/サーバーで多くのコードを実行した後、私は通常次のようなものを探します:

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が割り当てられている場合は常に破棄されるようにします...

1
K.Sandell

最も「正しい」バージョンはこれだと思います:

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;
0
dummzeuch