4

「純粋に文書化する」目的でメソッドで例外をキャッチして、メソッド自体の内部にエラー文書をカプセル化する必要がありますか、それとも呼び出し元の責任ですか?

フレームワークのメソッドを含め、自分のメソッドで他の多数のメソッドを呼び出すとします。これらのメソッドは、EncryptPackage()多数の例外をスローする可能性があります。すべてをブロックにラップするusingので、クリーンアップのために例外をキャッチする必要はありません (またはクリーンアップに try/finally を使用します)。とにかく例外をキャッチし、そのメソッドのコンテキストに関する詳細を提供する必要がありますか、それとも呼び出し元メソッドの責任ですか?

これがケース1です:

[Serializable]
class TestClassException : Exception
{
    public TestClassException() : base() { }
    public TestClassException(string message) : base(message) { }
    public TestClassException(string message, Exception innerException) : base(message, innerException) { }
}

class TestClass
{
    public TestClass() { }

    public void EncryptPackage()
    {
        try
        {
            DoSomething();
            DoAnotherThing();
        }
        catch (Exception ex)
        {
            throw new TestClassException("Error occurred during package encryption", ex);
        }
    }
}

class ConsumerExample
{
    public ConsumerExample() { }

    public void DoSomeStuff()
    {
        TestClass testClass = new TestClass();

        try
        {
            testClass.EncryptPackage();
        }
        catch (TestClassException ex)
        {
            System.Windows.Forms.MessageBox.Show(ex.ToString());
        }
    }
}

このコードではEncryptPackage()、「パッケージの暗号化中にエラーが発生しました」というテキストで「エラー テキストを装飾する」ためだけに、メソッドが考えられるすべての例外をキャッチする方法に注目してください。EncryptPackage()ここでは、エラーの説明ロジックをカプセル化します。

そして、ここに別のテクニックがあります:

class TestClass2
{
    public TestClass2() { }

    public void EncryptPackage()
    {
        DoSomething();
        DoAnotherThing();
    }
}

class ConsumerExample2
{
    public ConsumerExample2() { }

    public void DoSomeStuff()
    {
        TestClass testClass = new TestClass();

        try
        {
            testClass.EncryptPackage();
        }
        catch (Exception ex)
        {
            System.Windows.Forms.MessageBox.Show("Error occurred during package encryption.\r\n\r\n" + ex.ToString());
        }
    }
}

この例でEncryptPackage()は、呼び出し元が「パッケージの暗号化中にエラーが発生しました。\r\n\r\n」というメッセージでエラー ケースを文書化するため、何もキャッチしません。

これは非常に単純化された例であることに注意してください。実際には多数の階層クラスがあり、例外は長い呼び出しスタックを介して伝播します。例外をキャッチする方法としてどの方法が好まれるでしょうか? 2番目のアプローチは、「実際の処理」(ユーザーへの表示など)が行われるレイヤーで例外が処理されるため、「よりクリーン」に見えます。呼び出しスタック情報は例外オブジェクトに保存されるため、技術的には、例外がスローされた正確な場所を見つけることができます。しかし...それは、抽象化の各レベルがエラーに独自の説明を追加し、innerExceptionメンバーの前の例外を保持する最初のアプローチほど「十分に文書化」されていないようです。この場合、実行がTestClassレイヤーには、このクラス内で発生したエラーの詳細な説明が既に含まれています。したがって、これはエラー処理ロジックのより良いカプセル化のように感じます.

どちらを使用しますか?

4

4 に答える 4

4

これについては、Effective Java に次の章があります。

上位層は下位レベルの例外をキャッチし、代わりに、上位レベルの抽象化の観点から説明できる例外をスローする必要があります。このイディオムは、例外変換として知られています。

于 2012-10-01T08:40:26.307 に答える
2

私は2番目の例を好みます。これは主に、特にカスタム例外を記述している場合に、記述しなければならないエラー処理コードの量を大幅に減らすことができるためです。最初の例では、多くのカスタム例外クラスが生成されない可能性があります。多くの利点(例外がどこから来たのかを知らせるコールスタックがすでにあります)。

より説明的なエラーメッセージがあると便利だと思うかもしれませんが、誰がこれから恩恵を受けますか?エンドユーザー?ユーザーに例外メッセージを表示する必要がありますか(そしてどの言語を使用する予定ですか)?多くの場合、ユーザーは内部エラーが発生したことを知る必要があり、あきらめる(再起動する)か、再試行する必要があります。開発者にはメリットがありますか?とにかく、ソースコードが目の前にある状態でコールスタックを調べることになります。したがって、これ以上説明的なメッセージは必要ありません。その時点でコードが何をしているかを自分で確認できます。

これは厳格なルールではありません。ほとんどの場合、私はトップレベルで例外をキャッチするだけで、そこでログに記録してユーザーにエラーを報告します。例外をユーザーに直接報告している場合、多くの場合、元の例外は翻訳の恩恵を受けません。たとえば、存在しないファイルを開こうとした場合、System.IO.FileNotFoundExceptionは十分に説明的であるため、次のように翻訳する必要があります。他に何かありますか?そこにある膨大な数の例外すべてについて、本当に同じ判断を下しますか(「図書館の著者よりもよく知っているので、慎重に作成された例外を翻訳します」)。例外をキャッチするのは、例外から回復したい場合(通常は不可能)、またはごくまれに、より説明的な例外に変換したい場合のみです。

階層化アーキテクチャでは、レイヤー間で例外を変換することは理にかなっています。たとえば、データアクセスレイヤーから出てくる例外をアプリケーションレイヤーに適した形式にキャッチします。同様に、アプリケーションレイヤーとユーザーインターフェイスの間でも同様です。そのタイプのシステムで作業しているかどうかはわかりません。

例外を文書化する場合は、メソッドのxmlドキュメントで例外タグを使用する必要があります。これは、たとえばSandCastleを使用して、ドキュメントの一般的なヘルプファイルに使用できます。

于 2012-10-01T09:11:13.447 に答える
1

上記の @Sjoerd に従って、例外を変換して、同じレベルの抽象化にします。あなたの場合、 EncryptPackage は、呼び出し元ではなく、下位レベルの例外自体を変換する必要があります。

低レベルの例外が DB レイヤーからのものであるとします (DBException など)。呼び出し元は DBException を理解することを期待しますか? 答えは NO です。呼び出し元は、DBException ではなく、パッケージを暗号化したいのです。下位レベルの例外は、デバッグ目的で上位レベルの例外の内側にチェーンする必要があります。

最後に、TestClassException が例であることは知っていますが、例外クラスが問題を明確に説明していることを確認してください。

于 2012-10-01T09:58:02.350 に答える
0

簡単に区別できるいくつかの状況で試してキャッチする必要があります。

  • アプリのエントリ ポイント、UI イベント、マルチスレッド呼び出しなど、「外部」で呼び出すことができるメソッド。あなたが持っているすべてのキャッチにログ出力またはメッセージを入れてください。これにより、アプリのクラッシュを (ほとんどの場合) 回避できるだけでなく、問題の修正方法に関するフィードバックをあなたまたはユーザーに提供できます。

  • 本当に例外を処理できるとき。これは、たとえば、アプリがセカンダリ データベースまたはサーバー URL を選択したり、別の処理を適用したりできることを意味します。

  • たとえば、一時ファイルの削除に失敗しても、プロセスが失敗することはありません。

  • おそらくtry/catchが必要な場所が他にもいくつかありますが、これらはまれです

常にエラー処理を適切な方法でログに記録したり、ユーザーにメッセージを送信したりします。例外情報が消えないようにしてください。これは、アプリを取得する方法であり、「明確な理由なし」で適切に動作しないためです-少なくとも理由は次のとおりです。明らかにした。

また、例外を使用してワークフローを制御しないでください。他に何かを行う方法がまったくない場合を除き、実際に「スロー」するべきではありません。

于 2012-10-04T16:28:43.347 に答える