20

こんにちは、デルファイでネストされた try & finally ステートメントを実行する最良の方法は何ですか?

var cds1  : TClientDataSet;
    cds2  : TClientDataSet;
    cds3  : TClientDataSet;
    cds4  : TClientDataSet;
begin
  cds1      := TClientDataSet.Create(application );
  try
    cds2      := TClientDataSet.Create(application );
    try
      cds3      := TClientDataSet.Create(application );
      try
        cds4      := TClientDataSet.Create(application );
        try
        ///////////////////////////////////////////////////////////////////////
        ///      DO WHAT NEEDS TO BE DONE
        ///////////////////////////////////////////////////////////////////////
        finally
          cds4.free;
        end;

      finally
        cds3.free;
      end;
    finally
      cds2.free;
    end;
  finally
    cds1.free;
  end;
end;

これを行うためのより良い方法を提案できますか?

4

6 に答える 6

31

次はどうですか:

var cds1  : TClientDataSet;
    cds2  : TClientDataSet;
    cds3  : TClientDataSet;
    cds4  : TClientDataSet;
begin
  cds1      := Nil;
  cds2      := Nil;
  cds3      := Nil;
  cds4      := Nil;
  try
    cds1      := TClientDataSet.Create(nil);
    cds2      := TClientDataSet.Create(nil);
    cds3      := TClientDataSet.Create(nil);
    cds4      := TClientDataSet.Create(nil);
    ///////////////////////////////////////////////////////////////////////
    ///      DO WHAT NEEDS TO BE DONE
    ///////////////////////////////////////////////////////////////////////
  finally
    freeandnil(cds4);
    freeandnil(cds3);
    freeandnil(cds2);
    freeandnil(Cds1);
  end;
end;

これにより、コンパクトに保たれ、作成されたインスタンスのみを解放しようとします。失敗すると、最後にドロップして、提供した例のすべてのクリーンアップを実行するため、実際にはネストを実行する必要はありません。

個人的には、同じメソッド内にネストしないようにしています...例外は、try / try / exception/finallyシナリオです。ネストする必要がある場合は、別のメソッド呼び出しにリファクタリングすることを考える絶好の機会です。

編集mghieutkuによるコメントのおかげで少しクリーンアップされました。

EDITは、この例では必要ないため、オブジェクトの作成をアプリケーションを参照しないように変更しました。

于 2008-12-29T17:25:20.877 に答える
18

私は次のようなものを使用します:

var
  Safe: IObjectSafe;
  cds1 : TClientDataSet;
  cds2 : TClientDataSet;
  cds3 : TClientDataSet;
  cds4 : TClientDataSet;
begin
  Safe := ObjectSafe;
  cds1 := Safe.Guard(TClientDataSet.Create(nil)) as TClientDataSet;
  cds2 := Safe.Guard(TClientDataSet.Create(nil)) as TClientDataSet;
  cds3 := Safe.Guard(TClientDataSet.Create(nil)) as TClientDataSet;
  cds4 := Safe.Guard(TClientDataSet.Create(nil)) as TClientDataSet;
  ///////////////////////////////////////////////////////////////////////
  ///      DO WHAT NEEDS TO BE DONE
  ///////////////////////////////////////////////////////////////////////

  // if Safe goes out of scope it will be freed and in turn free all guarded objects
end;

インターフェイスの実装については、この記事を参照してください。ただし、似たようなものを自分で簡単に作成できます。

編集:

リンクされた記事で Guard() がプロシージャであることに気付きました。私自身のコードでは、TObject を返す Guard() 関数をオーバーロードしました。上記のサンプル コードは、似たようなものを想定しています。もちろん、ジェネリックを使用すると、はるかに優れたコードが可能になりました...

編集2:

なぜ try ... finally が私のコードで完全に削除されているのか疑問に思っている場合: メモリ リーク (デストラクタが例外を発生させた場合) またはアクセス違反の可能性を導入せずに、ネストされたブロックを削除することは不可能です。したがって、ヘルパー クラスを使用して、インターフェイスの参照カウントを完全に引き継ぐのが最善です。ヘルパー クラスは、一部のデストラクタで例外が発生した場合でも、保護しているすべてのオブジェクトを解放できます。

于 2008-12-29T17:38:47.743 に答える
4

ネストされた try のないコードの別のバリエーションがあります...ついにそれが私に起こりました。コンストラクターの AOwner パラメーターを nil に設定してコンポーネントを作成しない場合は、VCL が無料で提供するライフタイム管理を簡単に利用できます。

var
  cds1: TClientDataSet;
  cds2: TClientDataSet;
  cds3: TClientDataSet;
  cds4: TClientDataSet;
begin
  cds1 := TClientDataSet.Create(nil);
  try
    // let cds1 own the other components so they need not be freed manually
    cds2 := TClientDataSet.Create(cds1);
    cds3 := TClientDataSet.Create(cds1);
    cds4 := TClientDataSet.Create(cds1);

    ///////////////////////////////////////////////////////////////////////
    ///      DO WHAT NEEDS TO BE DONE
    ///////////////////////////////////////////////////////////////////////

  finally
    cds1.Free;
  end;
end;

私は小さなコードを大いに信じています (あまり難読化されていなければ)。

于 2008-12-30T09:29:59.383 に答える
3

この(IMO)醜いルート(解放が必要かどうかを知るために nil への初期化を伴うグループ処理)に行きたい場合は、少なくとも、デストラクタの1つで例外を発生させないことを保証する必要があります。あなたのオブジェクト。
何かのようなもの:

function SafeFreeAndNil(AnObject: TObject): Boolean;
begin
  try
    FreeAndNil(AnObject);
    Result :=  True;
  except
    Result := False;
  end;
end;

var cds1  : TClientDataSet;
    cds2  : TClientDataSet;
    IsOK1 : Boolean;
    IsOK2 : Boolean;
begin
  cds1      := Nil;
  cds2      := Nil; 
 try
    cds1      := TClientDataSet.Create(nil);
    cds2      := TClientDataSet.Create(nil);    
    ///////////////////////////////////////////////////////////////////////
    ///      DO WHAT NEEDS TO BE DONE
    ///////////////////////////////////////////////////////////////////////
  finally
    IsOk2 := SafeFreeAndNil(cds2);    // an error in freeing cds2 won't stop execution
    IsOK1 := SafeFreeAndNil(Cds1);
    if not(IsOk1 and IsOk2) then
      raise EWhatever....
  end;
end;
于 2009-03-23T09:59:09.060 に答える
2

コンストラクタとデストラクタの例外に関する良いビデオがあります

次のようないくつかの良い例を示しています。

var cds1  : TClientDataSet;
    cds2  : TClientDataSet;
begin
  cds1      := Nil;
  cds2      := Nil; 
 try
    cds1      := TClientDataSet.Create(nil);
    cds2      := TClientDataSet.Create(nil);    
    ///////////////////////////////////////////////////////////////////////
    ///      DO WHAT NEEDS TO BE DONE
    ///////////////////////////////////////////////////////////////////////
  finally
    freeandnil(cds2);    //// what has if there in an error in the destructor of cds2
    freeandnil(Cds1);
  end;
end;

cds2 のデストラクタにエラーがあればどうなるか

Cds1 は破棄されません

編集

別の優れたリソースは次のとおりです。

コード範囲 III の遅延例外処理に関する Jim McKeeth の優れたビデオでは、finally ブロックでの例外処理の問題について説明しています。

于 2009-01-10T10:05:33.483 に答える
1

@mghie: Delphi には、スタックに割り当てられたオブジェクトがあります。

type
  TMyObject = object
  private
    FSomeField: PInteger;
  public
    constructor Init;
    destructor Done; override;
  end;

constructor TMyObject.Init;
begin
  inherited Init;
  New(FSomeField);
end;

destructor TMyObject.Done;
begin
  Dispose(FSomeField);
  inherited Done;
end;

var
  MyObject: TMyObject;

begin
  MyObject.Init;
  /// ...
end;

残念ながら、上記の例が示すように: スタックに割り当てられたオブジェクトはメモリ リークを防ぎません。

したがって、これには、次のようなデストラクタへの呼び出しが必要です。

var
  MyObject: TMyObject;

begin
  MyObject.Init;
  try
    /// ...
  finally
    MyObject.Done;
  end;
end;

OK、認めます。これはほとんど話題から外れていますが、スタックに割り当てられたオブジェクトが解決策として言及されているため、このコンテキストでは興味深いかもしれないと思いました (自動デストラクタ呼び出しがない場合はそうではありません)。

于 2008-12-29T18:54:00.137 に答える