web-dev-qa-db-ja.com

C#:カスタム例外のスローのベストプラクティス

C#例外処理の実践に関する他の質問をいくつか読んだことがありますが、私が探しているものを尋ねる人はいません。

特定のクラスまたはクラスのセットに独自のカスタム例外を実装する場合。これらのクラスに関連するすべてのエラーは、内部例外を使用して例外にカプセル化する必要がありますか?

例外をソースからすぐに認識できるように、すべての例外をキャッチする方が良いと考えていました。私はまだ元の例外を内部例外として渡します。一方、例外を再スローすることは冗長になると考えていました。

例外:

class FooException : Exception
{
    //...
}

オプション1:Fooはすべての例外をカプセル化します

class Foo
{
    DoSomething(int param)
    {
        try 
        {
             if (/*Something Bad*/)
             {  
                 //violates business logic etc... 
                 throw new FooException("Reason...");
             }
             //... 
             //something that might throw an exception
        }
        catch (FooException ex)
        {
             throw;
        }
        catch (Exception ex)
        {
             throw new FooException("Inner Exception", ex);
        }
    }
}

オプション2:Fooは特定のFooExceptionをスローしますが、他の例外は通過できます:

class Foo
{
    DoSomething(int param)
    {
        if  (/*Something Bad*/)
        {
             //violates business logic etc... 
             throw new FooException("Reason...");
        }
        //... 
        //something that might throw an exception and not caught
    }
}
47
user295190

ライブラリでの私の経験に基づいて、いくつかの理由により、すべての(予想できる)をFooExceptionでラップする必要があります。

  1. 人々はそれがあなたのクラス、または少なくともそれらの使用法から来たことを知っています。彼らがFileNotFoundExceptionを見たら、それを探しているかもしれません。あなたは彼らがそれを絞り込むのを助けています。 (スタックトレースがこの目的を果たしていることを理解したので、この点を無視してもかまいません。)

  2. より多くのコンテキストを提供できます。 FNFを独自の例外でラップすると、「このファイルをロードしようとしましたこの目的のためで、見つかりませんでした。これは可能な解決策のヒントです。

  3. ライブラリはクリーンアップを正しく処理できます。例外をバブルさせると、ユーザーにクリーンアップを強制します。あなたがやっていたことを正しくカプセル化した場合、彼らは状況を処理する方法の手がかりを持ちません!

FileNotFoundのように、予想できる例外のみをラップすることを忘れないでください。 Exceptionを単にラップして最高のものを期待しないでください。

54
Tesserex

これをご覧ください MSDN-best-practises

キャッチされた例外を再スローする場合は、throw exの代わりにthrowを使用することを検討してください。この方法では、元のスタックトレースが保持されます(行番号など)。

18
Tim Schmelter

カスタム例外を作成するときは、常にいくつかのプロパティを追加します。 1つはユーザー名またはIDです。 DisplayMessageプロパティを追加して、ユーザーに表示するテキストを伝達します。次に、Messageプロパティを使用して、ログに記録される技術的な詳細を伝えます。

ストアドプロシージャの名前と渡されたパラメーターの値をキャプチャできるレベルで、データアクセスレイヤーのすべてのエラーをキャッチします。またはインラインSQL。データベース名または接続文字列の一部(資格情報なしでください)。それらは、Messageまたは独自の新しいカスタムDatabaseInfoプロパティに入れられます。

Webページでは、同じカスタム例外を使用します。 Messageプロパティにフォーム情報を入力します。ユーザーがWebページのすべてのデータ入力コントロールに入力した内容、編集中のアイテムのID(顧客、製品、従業員など)、およびユーザーのアクション例外が発生したときに取っていました。

だから、あなたの質問による私の戦略は次のとおりです。例外について何かできるときだけキャッチします。かなり頻繁に、私ができることは詳細を記録することだけです。そのため、これらの詳細が利用できるポイントでのみキャッチし、例外をUIにバブルさせるために再スローします。そして、カスタム例外に元の例外を保持します。

6
DOK

カスタム例外の目的は、デバッグを支援するためにスタックトレースに詳細なコンテキスト情報を提供することです。オプション1の方が優れています。オプション1がないと、スタック内で「より低い」例外が発生した場合に例外の「起源」を取得できないからです。

3
Dave Swersky

visual Studioで「例外」のコードスニペットを実行すると、例外を作成するための優れたプラクティスのテンプレートがあります。

1
Felice Pollano

オプション1:throw new FooException("Reason...");はtry/catchブロックの外側にあるため、キャッチされません

  1. 処理する例外のみをキャッチする必要があります。
  2. 例外に追加のデータを追加しない場合は、throw;スタックを殺さないので。オプション2では、まだcatch内で処理を行い、throw;元のスタックで元の例外を再スローします。
1
Jakub Konecki

例外をキャッチするときに知っておくべきコードの最も重要なことは、残念ながらExceptionオブジェクトから完全に欠落していますが、システムの状態が「あるべき」状態に関連しています(何かが間違っていたために例外がスローされたと思われます)。 LoadDocumentメソッドでエラーが発生した場合、おそらくドキュメントは正常にロードされていませんが、少なくとも2つのシステム状態が考えられます。

  1. システム状態は、ロードが試行されなかったかのようになります。この場合、ロードされたドキュメントがなくてもアプリケーションを続行できる場合、アプリケーションは完全に適切です。
  2. システム状態は十分に破損しているため、「回復」ファイルに保存できるものを保存して(ユーザーの適切なファイルを破損した可能性のあるデータに置き換えないで)シャットダウンするのが最善の処置です。

明らかに、これらの両極端の間に他の可能な状態があることがよくあります。状態#1が存在することを明示的に示すカスタム例外を作成するように努力することをお勧めします。発生し、状態1になる例外は、状態1を示す例外オブジェクトにラップする必要があります。システム状態が危険にさらされるような方法で例外が発生する可能性がある場合は、例外を#2としてラップするか、パーコレートを許可する必要があります。

1
supercat

オプション2が最適です。ベストプラクティスは、例外を使用して何かを行う場合にのみ例外をキャッチすることです。

この場合、オプション1は例外を独自の例外でラップしているだけです。値を追加せず、クラスのユーザーはArgumentExceptionをキャッチすることはできません。たとえば、FooExceptionをキャッチしてから内部例外を解析する必要もあります。内側の例外が例外でない場合、彼らは有用な何かをすることができ、彼らは再スローする必要があります。

0
btlog