37

私は次のコードを何度も見ました:

try
{
    ... // some code
}
catch (Exception ex)
{
    ... // Do something
    throw new CustomException(ex);

    // or
    // throw;

    // or
    // throw ex;
}

例外を再スローする目的を説明していただけますか?例外処理のパターン/ベストプラクティスに従っていますか?(「発信者通知」パターンと呼ばれることをどこかで読んだことがありますか?)

4

13 に答える 13

44

たとえば、例外をログに記録したいが処理はしたくない場合は、同じ例外を再度スローすると便利です。

キャッチされた例外をラップする新しい例外をスローすることは、抽象化に適しています。たとえば、あなたのライブラリは、あなたのライブラリのクライアントが知る必要のない例外をスローするサードパーティのライブラリを使用しています。その場合、それをライブラリによりネイティブな例外タイプにラップし、代わりにそれをスローします。

于 2008-09-22T07:15:12.193 に答える
26

実際には違いがあります

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);
    } 
}
于 2008-09-22T07:26:42.227 に答える
12

メソッドの実装の詳細を非表示にしたり、問題の抽象化レベルを向上させて、メソッドの呼び出し元にとってより意味のあるものにしたい場合があります。これを行うには、元の例外をインターセプトし、問題の説明により適したカスタム例外に置き換えることができます。

たとえば、要求されたユーザーの詳細をテキスト ファイルから読み込むメソッドを考えてみましょう。この方法では、ユーザーの ID と接尾辞「.data」が付いた名前のテキスト ファイルが存在することを前提としています。そのファイルが実際に存在しない場合、FileNotFoundException をスローしてもあまり意味がありません。各ユーザーの詳細がテキスト ファイルに格納されているという事実は、メソッド内部の実装の詳細であるためです。したがって、このメソッドは代わりに、元の例外を説明メッセージ付きのカスタム例外にラップすることができます。

表示されているコードとは異なり、ベスト プラクティスは、元の例外を新しい例外の InnerException プロパティとして読み込むことによって保持することです。これは、開発者が必要に応じて根本的な問題を分析できることを意味します。

カスタム例外を作成する場合、次のチェックリストが役立ちます。

• 例外がスローされた理由を伝える適切な名前を見つけ、その名前が「Exception」という語で終わっていることを確認します。

• 3 つの標準例外コンストラクターを必ず実装してください。

• Serializable 属性で例外をマークしていることを確認します。

• 逆シリアル化コンストラクターを実装していることを確認します。

• 開発者が例外をよりよく理解し、処理するのに役立つカスタム例外プロパティを追加します。

• カスタム プロパティを追加する場合は、必ず GetObjectData を実装してオーバーライドし、カスタム プロパティをシリアル化してください。

• カスタム プロパティを追加する場合は、Message プロパティをオーバーライドして、標準の例外メッセージにプロパティを追加できるようにします。

• カスタム例外の InnerException プロパティを使用して元の例外を添付することを忘れないでください。

于 2008-09-22T07:44:16.617 に答える
10

通常、コードがアプリケーション内のアーキテクチャ上のどこにあるかに応じて、2つの理由のいずれかでキャッチして再スローします。

アプリケーションの中核では、通常、例外をキャッチして再スローし、例外をより意味のあるものに変換します。たとえば、データアクセス層を作成していて、SQL Serverでカスタムエラーコードを使用している場合は、SqlExceptionをObjectNotFoundExceptionなどに変換できます。これは、(a)呼び出し元が特定の種類の例外を処理しやすくするため、および(b)SQL Serverを使用して他のレイヤーに永続的にリークするなど、そのレイヤーの実装の詳細を防ぐために役立ちます。将来、より簡単に物事を変更することができます。

アプリケーションの境界では、例外を変換せずにキャッチして再スローするのが一般的です。これにより、例外の詳細をログに記録して、ライブの問題のデバッグと診断に役立てることができます。理想的には、運用チームが簡単に監視できる場所(イベントログなど)と、開発者の制御フローで例外が発生した場所(通常はトレース)に関するコンテキストを提供する場所にエラーを公開する必要があります。

于 2008-09-22T07:25:52.807 に答える
2

私は次の理由を考えることができます:

  • APIの一部として、スローされた例外タイプのセットを固定しておくことで、呼び出し元が固定された例外のセットについてのみ心配する必要があります。Javaでは、チェックされた例外メカニズムのために、実際にはそれを行う必要があります。

  • 例外にいくつかのコンテキスト情報を追加します。たとえば、裸の「レコードが見つかりません」をDBから通過させる代わりに、それをキャッチして「...注文番号XXXの処理中に、製品YYYを探している」を追加することができます。

  • いくつかのクリーンアップを実行します-ファイルを閉じ、トランザクションをロールバックし、いくつかのハンドルを解放します。

于 2008-09-22T07:30:19.537 に答える
1

一般に、「何かをする」には、例外をより適切に説明する (たとえば、別の例外でラップする) か、特定のソースを通じて情報を追跡することが含まれます。

もう 1 つの可能性は、例外をキャッチする必要があるかどうかを判断するのに例外の種類だけでは不十分な場合です。

これは、メソッドが純粋に正当な理由で使用されていると言っているわけではありません。多くの場合、将来ある時点でトレース情報が必要になる可能性があると開発者が考える場合に使用されます。その場合、try {} catch {throw;} スタイルが取得されます。まったく役に立ちません。

于 2008-09-22T07:15:13.957 に答える
1

例外で何をしようとしているかに依存すると思います。

適切な理由の 1 つは、最初にエラーをキャッチに記録し、それを UI にスローしてわかりやすいエラー メッセージを生成し、元のエラーを含むエラーのより「詳細な」ビューを表示するオプションを使用することです。 .

もう 1 つのアプローチは「再試行」アプローチです。たとえば、エラー カウントが保持され、エラーがスタックに送信されるのは、一定量の再試行の後でのみです (これは、タイムアウトしたデータベース呼び出しのデータベース アクセスに対して行われることがあります。低速ネットワーク経由で Web サービスにアクセスする場合)。

ただし、それを行う理由は他にもたくさんあります。

于 2008-09-22T07:15:14.517 に答える
1

参考までに、これは再スローの各タイプに関する関連する質問です: 例外をスローするためのパフォーマンスに関する考慮事項

私の質問は、例外を再スローする「理由」と、アプリケーションの例外処理戦略におけるその使用法に焦点を当てています。

于 2008-09-22T07:17:52.140 に答える
0

EntLib ExceptionBlockの使用を開始するまで、エラーをスローする前にログに記録するために使用していました。その時点で私がそれらを処理できたと思うとちょっと厄介ですが、当時は、フローオンバグをカバーするよりも、UATで(ログに記録した後)それらをひどく失敗させる方が良かったです。

于 2008-09-22T07:13:00.903 に答える
0

アプリケーションは、おそらくコールスタックの上位でこれらの再スローされた例外をキャッチするため、それらを再スローすると、上位のハンドラーがそれらを適切にインターセプトして処理できるようになります。アプリケーションには、期待値をログに記録または報告するトップレベルの例外ハンドラーがあるのが非常に一般的です。

もう1つの方法は、コーダーが怠惰であり、処理したい例外のセットだけをキャッチする代わりに、すべてをキャッチしてから、実際に処理できないものだけを再スローすることです。

于 2008-09-22T07:14:18.077 に答える
0

メソッドの結果を取得する別の方法として例外を見ると、例外を再スローすることは、結果を他のオブジェクトにラップするようなものです。

そして、これは非例外的な世界では一般的な操作です。通常、これは 2 つのアプリケーション レイヤーの境界で発生します。レイヤーの関数がレイヤーBの関数を呼び出すと、の結果がの内部形式Cに変換されます。CB

A -- calls --> B -- calls --> C

そうでない場合は、レイヤーAを呼び出すレイヤーBで、処理する JDK 例外の完全なセットが存在します。:-)

また、受け入れられた回答が指摘しているように、レイヤーはの例外Aにさえ気付いていない可能性があります。C

レイヤー A、サーブレット: 画像とそのメタ情報を取得します
レイヤー B、JPEG ライブラリ: JPEG ファイルを解析するために使用可能な DCIM タグを収集します
レイヤー C、単純な DB: ランダム アクセス ファイルから文字列レコードを読み取るクラス。一部のバイトが破損しているため、「レコード 'bibliographicCitation' の UTF-8 文字列を読み取れません」という例外がスローされます。

だからA、「bibliographicCitation」の意味を理解できません。したがってB、この例外を変換ATagsReadingExceptionて、オリジナルをラップする必要があります。

于 2013-11-06T23:24:25.077 に答える
0

Rafal が述べたように、これは、チェックされた例外をチェックされていない例外、または API により適したチェックされた例外に変換するために行われることがあります。ここに例があります:

http://radio-weblogs.com/0122027/stories/2003/04/01/JavasCheckedExceptionsWereAMitake.html

于 2010-07-30T16:52:44.743 に答える
-3

例外を再スローする主な理由は、コール スタックをそのままにしておくことです。これにより、何が発生し、シーケンスを呼び出すかをより完全に把握できます。

于 2008-09-22T07:17:50.847 に答える