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
}
}
ライブラリでの私の経験に基づいて、いくつかの理由により、すべての(予想できる)をFooException
でラップする必要があります。
人々はそれがあなたのクラス、または少なくともそれらの使用法から来たことを知っています。彼らがFileNotFoundException
を見たら、それを探しているかもしれません。あなたは彼らがそれを絞り込むのを助けています。 (スタックトレースがこの目的を果たしていることを理解したので、この点を無視してもかまいません。)
より多くのコンテキストを提供できます。 FNFを独自の例外でラップすると、「このファイルをロードしようとしましたこの目的のためで、見つかりませんでした。これは可能な解決策のヒントです。
ライブラリはクリーンアップを正しく処理できます。例外をバブルさせると、ユーザーにクリーンアップを強制します。あなたがやっていたことを正しくカプセル化した場合、彼らは状況を処理する方法の手がかりを持ちません!
FileNotFound
のように、予想できる例外のみをラップすることを忘れないでください。 Exception
を単にラップして最高のものを期待しないでください。
これをご覧ください MSDN-best-practises 。
キャッチされた例外を再スローする場合は、throw ex
の代わりにthrow
を使用することを検討してください。この方法では、元のスタックトレースが保持されます(行番号など)。
カスタム例外を作成するときは、常にいくつかのプロパティを追加します。 1つはユーザー名またはIDです。 DisplayMessageプロパティを追加して、ユーザーに表示するテキストを伝達します。次に、Messageプロパティを使用して、ログに記録される技術的な詳細を伝えます。
ストアドプロシージャの名前と渡されたパラメーターの値をキャプチャできるレベルで、データアクセスレイヤーのすべてのエラーをキャッチします。またはインラインSQL。データベース名または接続文字列の一部(資格情報なしでください)。それらは、Messageまたは独自の新しいカスタムDatabaseInfoプロパティに入れられます。
Webページでは、同じカスタム例外を使用します。 Messageプロパティにフォーム情報を入力します。ユーザーがWebページのすべてのデータ入力コントロールに入力した内容、編集中のアイテムのID(顧客、製品、従業員など)、およびユーザーのアクション例外が発生したときに取っていました。
だから、あなたの質問による私の戦略は次のとおりです。例外について何かできるときだけキャッチします。かなり頻繁に、私ができることは詳細を記録することだけです。そのため、これらの詳細が利用できるポイントでのみキャッチし、例外をUIにバブルさせるために再スローします。そして、カスタム例外に元の例外を保持します。
カスタム例外の目的は、デバッグを支援するためにスタックトレースに詳細なコンテキスト情報を提供することです。オプション1の方が優れています。オプション1がないと、スタック内で「より低い」例外が発生した場合に例外の「起源」を取得できないからです。
visual Studioで「例外」のコードスニペットを実行すると、例外を作成するための優れたプラクティスのテンプレートがあります。
注オプション1:throw new FooException("Reason...");
はtry/catchブロックの外側にあるため、キャッチされません
throw;
スタックを殺さないので。オプション2では、まだcatch内で処理を行い、throw;
元のスタックで元の例外を再スローします。例外をキャッチするときに知っておくべきコードの最も重要なことは、残念ながらExceptionオブジェクトから完全に欠落していますが、システムの状態が「あるべき」状態に関連しています(何かが間違っていたために例外がスローされたと思われます)。 LoadDocumentメソッドでエラーが発生した場合、おそらくドキュメントは正常にロードされていませんが、少なくとも2つのシステム状態が考えられます。
明らかに、これらの両極端の間に他の可能な状態があることがよくあります。状態#1が存在することを明示的に示すカスタム例外を作成するように努力することをお勧めします。発生し、状態1になる例外は、状態1を示す例外オブジェクトにラップする必要があります。システム状態が危険にさらされるような方法で例外が発生する可能性がある場合は、例外を#2としてラップするか、パーコレートを許可する必要があります。
オプション2が最適です。ベストプラクティスは、例外を使用して何かを行う場合にのみ例外をキャッチすることです。
この場合、オプション1は例外を独自の例外でラップしているだけです。値を追加せず、クラスのユーザーはArgumentExceptionをキャッチすることはできません。たとえば、FooExceptionをキャッチしてから内部例外を解析する必要もあります。内側の例外が例外でない場合、彼らは有用な何かをすることができ、彼らは再スローする必要があります。