私は次のような種類のコードに何度も遭遇したことがありますが、これが(パフォーマンスの観点から見て)良い方法であるかどうか疑問に思います。
try
{
... // some code
}
catch (Exception ex)
{
... // Do something
throw new CustomException(ex);
}
基本的に、コーダーがしていることは、彼らがカスタム例外で例外を包含していて、それを再び投げているということです。
これは、パフォーマンスにおいて次の2つとどのように異なりますか。
try
{
... // some code
}
catch (Exception ex)
{
.. // Do something
throw ex;
}
または
try
{
... // some code
}
catch (Exception ex)
{
.. // Do something
throw;
}
機能的またはコーディングに関するベストプラクティスの議論を脇に置いて、3つのアプローチの間にパフォーマンスの違いはありますか?
@Brad Tutterow
最初のケースで例外が失われることはなく、コンストラクタに渡されます。私は残りの部分であなたと同意しますが、2番目の方法はスタックトレースが失われるため非常に悪い考えです。私が.NETを使用していたとき、他のプログラマーがそれをしていた多くの場合に出くわしました、そして例外の本当の原因を知る必要があったときに限りなく私を失望させました。私は今問題がどこで起きたのかわかりません。
私はまたあなたがパフォーマンスについて心配するべきではないとの2番目のBradのコメントもします。この種のマイクロ最適化は難しい考えです。長期間実行されているforループのすべての繰り返しで例外をスローすることについて話しているのでない限り、例外の使用方法によってパフォーマンスの問題に遭遇することはおそらくないでしょう。
パフォーマンスを最適化する必要があることを示す指標がある場合は、常にパフォーマンスを最適化してから、犯人であることが証明されている点に到達します。
何かをナノ秒速く走らせるよりも、簡単なデバッグ機能(スタックトレースを隠さないIE)を備えた読みやすいコードを持つ方がはるかに良いです。
例外をカスタム例外にラップすることについての最後のメモ...これは、特にUIを扱うときに非常に便利な構成体になることがあります。あなたはすべての既知で合理的な例外ケースを何らかの基本カスタム例外(あるいはその基本例外から拡張するもの)にラップすることができ、そしてUIはこの基本例外を捉えることができます。捕捉されたとき、例外はユーザーに情報を表示する手段、例えばReadableMessageプロパティ、またはそれらの線に沿った何かを提供する必要があるでしょう。したがって、UIが例外を見逃したときはいつでも、修正が必要なバグのためであり、例外をキャッチしたときはいつでも、UIによって適切に処理され、処理されるべき既知のエラー状態です。
明らかに、新しいオブジェクトを作成することによるペナルティ(新しいException)が発生するため、プログラムに追加するすべてのコード行を処理するときとまったく同じように、例外の分類を増やすことで余分な作業が必要かどうかを判断する必要があります。
その決定を下すためのアドバイスとして、新しいオブジェクトがその例外についての追加の情報を持っていない場合は、新しい例外の作成を忘れることができます。
しかしながら、他の状況では、例外の階層を持つことはあなたのクラスのユーザにとって非常に便利です。ファサードパターンを実装しているとします。これまで検討してきたシナリオはどちらも適切ではありません。
この仮想的なケースでは、システムの内部の複雑さからユーザーを抽象化し、生成された例外の種類について何かを知ることができるようにする例外クラスの階層を作成することをお勧めします。
補足として:
個人的には、ロジックを実装するための例外(Exceptionクラスから派生したクラスの階層)の使用は嫌いです。ケースのように:
try {
// something that will raise an exception almost half the time
} catch( InsufficientFunds e) {
// Inform the customer is broke
} catch( UnknownAccount e ) {
// Ask for a new account number
}
Davidのように、2番目と3番目のほうがパフォーマンスが良いと思います。しかし、3人のうちの1人は、それについて心配する時間を費やすには不十分なパフォーマンスを示すでしょうか。パフォーマンスよりも大きな問題があると思います。
FxCopは常にオリジナルのスタックトレースが失われないように、2番目より3番目のアプローチを推奨します。
編集:明らかに間違っていたものを削除し、マイクは指摘するのに十分親切でした。
しないでください。
try
{
// some code
}
catch (Exception ex) { throw ex; }
これがスタックトレースを失うので。
代わりにしてください。
try
{
// some code
}
catch (Exception ex) { throw; }
ただスローするだけです、新しいカスタム例外の内部例外にしたい場合は、例外変数を渡すだけです。
他の人が述べているように、あなたはただ既存のオブジェクトを再投げているので、最高のパフォーマンスは一番下のものから来る。真ん中のものは、スタックを失うので、最も正確ではありません。
コード内の特定の依存関係を分離したい場合は、私は個人的にカスタム例外を使用します。たとえば、XMLファイルからデータをロードするメソッドがあります。これはさまざまな方法でうまくいかない可能性があります。
ディスクからの読み取りに失敗し(FileIOException)、許可されていない場所からアクセスしようとした場合(SecurityException)、ファイルが破損している場合(XmlParseException)、データの形式が間違っている場合(DeserialisationException)。
この場合、呼び出し側クラスはこれらすべてを理解するのが簡単なので、これらすべての例外は単一のカスタム例外(FileOperationException)を再スローします。つまり、呼び出し元はSystem.IOまたはSystem.Xmlへの参照を必要としません。どのようなエラーが発生したかにアクセスするには、列挙型と重要な情報を使用します。
述べたように、このようなものをマイクロ最適化しようとしないでください、まったく例外をスローするという行為はここで起こる最も遅いものです。行うための最善の改善は、まったく例外を回避することです。
public bool Load(string filepath)
{
if (File.Exists(filepath)) //Avoid throwing by checking state
{
//Wrap anyways in case something changes between check and operation
try { .... }
catch (IOException ioFault) { .... }
catch (OtherException otherFault) { .... }
return true; //Inform caller of success
}
else { return false; } //Inform caller of failure due to state
}
最初の例のスローは、新しいCustomExceptionオブジェクトの作成によるオーバーヘッドを持ちます。
2番目の例の再スローは、Exception型の例外をスローします。
3番目の例の再スローは、「いくつかのコード」によってスローされたのと同じタイプの例外をスローします。
そのため、2番目と3番目の例では使用するリソースが少なくなります。
ちょっと待ってください。例外が発生した場合、なぜパフォーマンスが気になるのでしょうか。通常のアプリケーションフローの一部として例外を使用しているのでない限り(これはベストプラクティスに反してWAYYYYです)。
私は成功に関してのみパフォーマンス要件を見ましたが、失敗に関しては決して見ませんでした。
純粋にパフォーマンスの観点から、3番目のケースが最もパフォーマンスが高いと思います。他の2つは、スタックトレースを抽出して新しいオブジェクトを作成する必要がありますが、どちらもかなり時間がかかる可能性があります。
これら3つのコードブロックには非常に異なる(外部の)振る舞いなので、それらを比較することは、赤黒の木に項目を追加するよりもQuickSortが効率的かどうかを尋ねるようなものです。正しいことを選択することほど重要ではありません。