Delphi例外処理-適切にクリーンアップする方法?

delphi exception-handling
Delphi例外処理-適切にクリーンアップする方法?

私たちのアプリケーションでいくつかのコードを見ていると、私が普段やっていることから少し奇妙な何かに出くわしました。 例外処理とクリーンアップでは、(他の多くのプログラマーと同様に)確かに、Try / Exceptブロックに埋め込まれたTry / Finallyブロックを使用します。 今、私はTry / Finally内のTry / Exceptに次のように慣れています:

Try
  Try
    CouldCauseError(X);
  Except
    HandleError;
  end;
Finally
  FreeAndNil(x);
end;

しかし、この他のコードブロックは次のように逆になります。

Try
  Try
    CouldCauseError(X);
  Finally
    FreeAndNil(x);
  end;
Except
  HandleError;
end;

Webを見てみると、これを両方の方法で行っている人がいますが、その理由については説明がありません。 私の質問は、どちらが外側のブロックを取得し、どちらが内側のブロックを取得するかということです。 または、どのように構成されていても、exceptセクションとfinallyセクションが処理されますか? ありがとう。

  10  2


ベストアンサー

1つの違いは、try..finally..exceptが例外マスキング状況に対して潜在的に脆弱であることです。

  • CouldCauseError()で例外が発生すると想像してください。 次に、*最終的に FreeAndNIL (X)を試みると、さらに例外が発生することを想像してください。 元の例外( FreeAndNIL *()例外につながる不安定性につながる可能性が非常に高い)は失われます。 * except *ハンドラーは、元の例外の後に発生した「ダウンストリーム」例外を処理するようになりました。

  • try..except..finally *はもちろんこれを回避し、この理由で優先されるべきです(例外をソースにできるだけ近づけて対処する)。

このような単純なケース(クリーンアップされる単一のオブジェクト)を処理する別の方法は、例外ハンドラーの通常のフロー* and *の両方にクリーンアップを含めることです。

try
  CouldCauseError(X);
  FreeAndNil(x);
except
  HandleError;
  FreeAndNil(x);
end;

これは最初は少し怖いようです(「* FreeAndNIL (X)が呼び出されることを確認する必要があるので、 FINALLY * !!を持っている必要があります」)、最初のFreeAndNIL()がそうでない唯一の方法例外があり、例外がある場合はFreeAndNIL()であり、例外が発生した場合のクリーンアップの順序を少し明確にします(ある程度ノイズを除去するという意味で)何が起こっているのかを理解するために「フィルタリング」する必要があります)。

しかし、私は個人的にそれが好きではありません-例外ハンドラまたは通常のフローのいずれかでコードを変更すると、クリーンアップ動作を壊す危険がありますが、そのようなブロックの周りのコードとブロック自体のサイズに応じて、 「ノイズ」は、単純化のために、場合によっては正当化されると主張できます。

ただし、これは* FreeAndNIL ()が実際には「 NILThenFree ()」であるという事実に依存しています…​ * X *は解放される前にNILされるため、通常フローの FreeAndNIL (X)で例外が発生した場合、例外ハンドラが X.Freeによって発生した例外をキャッチするとXはNILになります。 *したがって、Xを「ダブルフリー」にしようとしません。

あなたが決めるものは何でも、私はそれが役立つことを願っています。

5


最終的にも例外も両方トリガーされ、順序はあなた次第です。 最終ブロックまたは除外ブロックで何をしたいかによって異なります。 exceptブロックで使用されているものを解放しますか? 最後に、exceptブロックの周りに配置します。

4


finallyブロック内のコードが例外自体を発生させることができるか(上位例外を除いて保護する必要がある)、または後で解放する必要がある例外処理で何かが必要かどうか(すべてが必要です) 上位ブロックで最終的にブロック)されます。

つまり、次のようなコードを作成できる場合もあります。

  try
    try
      try
        CouldCauseError(X);
      except
        HandleErrorWith(X);
      end;
    finally
      FreeAndNil(X); // and/or any resource cleanup
    end;
  except
    CatchAllError;
  end;

2


最初は、コードが少し奇妙に見えます。 Xの作成が恋しいです。

X := CreateAnX
try
  DoSomeThing(X);
finally
  FreeAndNil(x);
end;

大事です。 このようなコードがある場合

// don't do something like this
try
  X := CreateAnX
  DoSomeThing(X);
finally
  FreeAndNil(x);
end;

あなたは*幸運*であり、それは機能します。 しかし、構築が失敗した場合は、「* _ lucky_ *」になってアクセス違反を取得するか、または後で運が悪く*完全に異なるコード位置でアクセス違反を取得する可能性があります。

代替案は

X := nil;
try
  X := CreateAnX
  DoSomeThing(X);
finally
  FreeAndNil(x);
end;
  • except *を使用する場所は、ユーザーの意図によって異なります。 すべての例外をキャッチし、すべての呼び出しコードが問題を解決することを知りたい場合(最終的にtryを使用)、外側のexcept-blockが方法です

try
  X := CreateAnX
  try
    DoSomeThing(X);
  finally
    FreeAndNil(x);
  end;
except
  on e: Exception do
    LogException(e)
end;

しかし、すべてのエラーをキャッチしたいときは常に考えてください。 例として(そして、私はしばしば間違っていると思う)、Indy OnExecuteハンドラーでこのようにしないでください。 そこでは、このようなものを使用する必要があります

try
  X := CreateAnX
  try
    DoSomeThing(X);
  finally
    FreeAndNil(x);
  end;
except
  on EIdException do
    raise;
  on e: Exception do
    LogException(e)
end;

例外をスローするか、(例として)会話が失敗する可能性があるために例外を予期する場合、エラーをキャッチするための最も内側の位置を探します。

X := CreateAnX
try
  DoSomeThing(X);
  try
    i := StrToInt(X.Text);
  except
    on EConvertError do
      i := 0;
  end;
finally
  FreeAndNil(x);
end;

このようにしないでください

X := CreateAnX
try
  try
    DoSomeThing(X);
    i := StrToInt(X.Text);
  except
    on EConvertError do
      i := 0;
  end;
finally
  FreeAndNil(x);
end;

DoSomeThingでもEConvertErrorが予想される場合にのみ、これを行ってください。 DoSomeThingがEConvertErrorをスローし、予期しない場合、コードには重大な問題があり、修正する必要があります。 この状況では、ユーザーが作業を保存できるようにし(おそらく、作業が破損する可能性があるため、コピーとして)、問題に関する情報を取得できるようにします。

少なくとも、例外を隠すためではなく、例外を処理するために試してください。

0


タイトルとURLをコピーしました