私は次のようなコードを何度も見ました。
try
{
... // some code
}
catch (Exception ex)
{
... // Do something
throw new CustomException(ex);
// or
// throw;
// or
// throw ex;
}
例外を再スローする目的を説明してください。例外処理のパターン/ベストプラクティスに従っていますか? (「Caller Inform」パターンと呼ばれる箇所をどこかで読んだことがありますか。)
同じ例外を再スローすることは、たとえば例外をログに記録するがそれを処理したくない場合に役立ちます。
キャッチされた例外をラップする新しい例外をスローすることは抽象化に適しています。たとえば、あなたのライブラリは、あなたのライブラリのクライアントが知るべきではないという例外を投げるサードパーティライブラリを使用しています。その場合、あなたはそれをあなたのライブラリにもっとネイティブな例外タイプにラップし、代わりにそれを投げます。
実際には違いがあります
throw new CustomException(ex);
そして
throw;
2番目はスタック情報を保存します。
ただし、DatabaseExceptionをGUIに到達させるのではなく、例外をアプリケーションドメインにとってより「親しみやすい」ものにしたい場合は、元の例外を含むカスタム例外を発生させます。
例えば:
try
{
}
catch (SqlException ex)
{
switch (ex.Number) {
case 17:
case 4060:
case 18456:
throw new InvalidDatabaseConnectionException("The database does not exists or cannot be reached using the supplied connection settings.", ex);
case 547:
throw new CouldNotDeleteException("There is a another object still using this object, therefore it cannot be deleted.", ex);
default:
throw new UnexpectedDatabaseErrorException("There was an unexpected error from the database.", ex);
}
}
メソッドの実装の詳細を隠したい、または改善したいことがあります。 呼び出し側にとってより意味のあるものにするための、問題の抽象化レベル メソッドのこれを行うには、元の例外を傍受して代用することができます。 問題を説明するのにより適しているカスタム例外。
たとえば、要求されたユーザーの詳細をテキストファイルから読み込む方法を取ります。この方法では、ユーザーのIDと接尾辞「.data」で名前が付けられたテキストファイルが存在すると想定しています。そのファイルが実際には存在しない場合は、FileNotFoundExceptionをスローしても意味がありません。これは、各ユーザーの詳細がテキストファイルに格納されているという事実が、メソッド内部の実装の詳細だからです。そのため、このメソッドでは代わりに、元の例外を説明メッセージ付きのカスタム例外で囲むことができます。
ここに示したコードとは異なり、ベストプラクティスは、元の例外を新しい例外のInnerExceptionプロパティとしてロードして保持することです。つまり、開発者は必要に応じて根本的な問題を分析できます。
カスタム例外を作成している場合は、これが便利なチェックリストです。
•例外がスローされた理由を伝える適切な名前を見つけ、名前が「Exception」という単語で終わっていることを確認します。
•必ず3つの標準例外コンストラクタを実装してください。
•必ず例外をSerializable属性でマークしてください。
•必ず逆シリアル化コンストラクターを実装してください。
•開発者が自分の例外をよりよく理解して処理するのに役立つ可能性のあるカスタム例外プロパティを追加します。
•カスタムプロパティを追加する場合は、必ずGetObjectDataを実装してオーバーライドし、カスタムプロパティをシリアル化してください。
•カスタムプロパティを追加する場合は、自分のプロパティを標準の例外メッセージに追加できるようにMessageプロパティをオーバーライドします。
?カスタム例外のInnerExceptionプロパティを使って、元の例外を添付するのを忘れないでください。
通常、コードがアプリケーション内のどこにアーキテクチャ的に配置されているかに応じて、2つの理由のいずれかでキャッチして再スローします。
アプリケーションの中核として、通常は例外をより意味のあるものに変換するためにキャッチして再スローします。たとえば、SQL Serverでデータアクセスレイヤを作成してカスタムエラーコードを使用している場合は、SqlExceptionをObjectNotFoundExceptionのようなものに変換することができます。これは、(a)呼び出し元が特定の種類の例外を処理しやすくするため、および(b)永続化にSQL Serverを使用しているという他の層への漏洩など、その層の実装の詳細を防ぐために便利です。将来、もっと簡単に変更することができます。
アプリケーションの境界では、例外の詳細をログに記録できるように例外を変換せずにキャッチして再スローするのが一般的です。デバッグや実際の問題の診断に役立ちます。理想的には、運用チームが簡単に監視できる場所(イベントログなど)と、開発者向けの制御フローで例外が発生した場所の前後関係を示す場所(通常はトレース)のどちらかにエラーを公開します。
次のような理由が考えられます。
APIの一部として、スローされた例外タイプのセットを固定しておくことで、呼び出し元が固定された例外のセットについて心配するだけで済みます。 Javaでは、チェックされた例外メカニズムのために、実際にはそうすることを強いられます。
例外にコンテキスト情報を追加します。たとえば、裸の「レコードが見つかりません」をDBから通過させるのではなく、それをキャッチして「...の注文番号XXXを処理している間に製品YYYを検索する」を追加することができます。
ファイルのクローズ、トランザクションのロールバック、ハンドルの解放などのクリーンアップを実行します。
一般に、「何かをする」ことは、例外をよりよく説明すること(例えば、それを別の例外でラップすること)、または特定のソースを通して情報をトレースすることのどちらかを含みます。
別の可能性は、例外の種類が例外を捕捉する必要があるかどうかを知るのに十分な情報ではない場合です。その場合、それを捕捉することを調べることはより多くの情報を提供します。
これは純粋に正当な理由でメソッドが使用されることを意味するのではなく、開発者がトレース情報が将来必要になると思われるときに使用されることが多いのです。まったく役に立ちません。
それはあなたが例外をどうしようとしているかにかかっていると思う。
1つの正当な理由は、最初にエラーをキャッチに記録してから、UIに投げて、エラーの「詳細/詳細」ビューを表示するオプション付きのわかりやすいエラーメッセージを生成することです。これには元のエラーが含まれます。 。
もう1つのアプローチは「再試行」アプローチです。たとえば、エラーカウントを保持し、エラーがスタックに送信される唯一の時間である一定の再試行回数後にタイムアウトになるデータベース呼び出しのデータベースアクセスに対して行われます。遅いネットワーク上でWebサービスにアクセスする際に)
他にもたくさんの理由があります。
参考までに、これは各タイプの再スローに関する関連質問です。例外をスローするためのパフォーマンス上の考慮事項
私の質問は、「なぜ」という理由と、アプリケーションの例外処理戦略における例外の使用方法について説明します。
EntLib ExceptionBlockを使い始めるまでは、投げる前にエラーを記録するためにそれらを使っていました。その時点でそれらを処理できたと思うときは厄介ですが、当時は、それらをUATで(それらをログに記録した後で)気まぐれに失敗させる方が、流れてくるバグをカバーするよりはましでした。
アプリケーションは、おそらく呼び出しスタックの上位にあるこれらの再スローされた例外をキャッチします。したがって、それらを再スローすると、その上位のハンドラーが適切にそれらをインターセプトして処理することができます。アプリケーションが期待値を記録または報告するトップレベルの例外ハンドラを持つことは非常に一般的です。
もう1つの選択肢は、コーダーが怠惰で、処理したい一連の例外だけをキャッチするのではなく、すべてをキャッチしてから、実際に処理できないものだけを再度スローすることです。
Rafalが述べたように、時にはこれはチェックされた例外をチェックされていない例外、またはAPIにより適したチェックされた例外に変換するために行われます。ここに例があります:
http://radio-weblogs.com/0122027/stories/2003/04/01/JavasCheckedExceptionsWereAMistake.html
あなたが上のように例外を見ればメソッドの結果を取得するための代替方法その後、例外を再スローするのは、結果を他のオブジェクトにラップするのと同じです。
そしてこれは非例外的な世界では一般的な操作です。通常、これは2つのアプリケーション層の境界で発生します。B
layerから関数を呼び出すC
変換するC
の結果B
の内部形式
A -- calls --> B -- calls --> C
そうでなければ、その時層でA
これはレイヤを呼び出しますB
処理するべきJDK例外のフルセットがあるでしょう。 :-)
受け入れられた答えも指摘するように、層A
気付いていないかもしれませんC
の例外です。
例
レイヤーA、サーブレット:画像とそのメタ情報を取得します
レイヤーB、JPEGライブラリ:JPEGファイルを解析するために利用可能なDCIMタグを収集します。
レイヤC単純なDB:ランダムアクセスファイルから文字列レコードを読み込むクラス。一部のバイトが壊れているため、「レコード 'bibliographicCitation'のUTF-8文字列を読み取れません」という例外がスローされます。
そうA
「bibliographicCitation」の意味を理解できません。そうB
この例外をA
にTagsReadingException
オリジナルをラップします。
例外を再スローする主な理由は、Call Stackをそのままにしておくことです。そのため、何が起きて呼び出しシーケンスが行われるのかをより完全に把握することができます。