例外の処理には、多くの相対性が関係しています。例外がハードウェアと OS から発生したエラーをカバーする低レベル API を超えて、何が例外を構成し、何が正常な状態であるかをプログラマーが決定する怪しげな領域があります。
例外をいつ使用するかをどのように決定しますか? 例外に関する一貫したポリシーはありますか?
例外の処理には、多くの相対性が関係しています。例外がハードウェアと OS から発生したエラーをカバーする低レベル API を超えて、何が例外を構成し、何が正常な状態であるかをプログラマーが決定する怪しげな領域があります。
例外をいつ使用するかをどのように決定しますか? 例外に関する一貫したポリシーはありますか?
例外は、オブジェクト内のメソッド間で内部的に情報を渡す方法として使用しないでください。ローカルでは、エラーコードと防御プログラミングを使用する必要があります。
例外は、エラーが検出されたポイントからエラーを処理できる場所(スタックの上位)に制御を渡すように設計されています。これはおそらく、ローカルコードに問題を修正するための十分なコンテキストがなく、スタックの上位にあるものが原因です。より多くのコンテキストがあり、したがって回復をより適切に整理できるようになります。
(少なくともC ++では)例外を検討するときは、APIが行う例外の保証を検討する必要があります。保証の最低レベルは基本保証である必要がありますが、強力な保証を提供するように(適切な場合は)努力する必要があります。関節APIからの外部依存関係を使用しない場合は、スローなしの保証を提供しようとすることもできます。
注意:例外保証と例外仕様を混同しないでください。
例外がメソッドをエスケープした後のオブジェクトの状態についての保証はありません。これらの状況では、オブジェクトは使用されるべきではありません。
ほとんどすべての状況で、これはメソッドが提供する最小の保証である必要があります。これにより、オブジェクトの状態が明確に定義され、引き続き一貫して使用できることが保証されます。
これにより、メソッドが完全に正常に実行されるか、例外がスローされてオブジェクトの状態が変更されないことが保証されます。
このメソッドは、メソッドから例外が伝播されないことを保証します。すべてのデストラクタはこの保証を行う必要があります。
| 注意:例外がすでに伝播しているときに例外がデストラクタをエスケープする場合
| アプリケーションは終了します
マイクロソフトのシニアソフトウェアデザインエンジニアであるEricLippertによるこのブログエントリは、例外戦略ガイドラインの優れた簡潔なセットをまとめたものです。
要するに:
致命的:プロセスが完全に回復不能であることを示すひどいエラー。できる限りのリソースをクリーンアップしますが、それらをキャッチしないでください。このような状況を検出する機能を備えたコードを作成している場合は、必ずスローしてください。例:メモリ不足の例外。
Boneheaded:処理が渡されているデータを操作できないことを示す比較的単純なエラーですが、エラーの原因となった状況が単に無視された場合は通常どおり続行されます。これらはバグとしてよく知られています。それらを投げたりキャッチしたりしないでください。代わりに、通常はエラーや、メソッドで処理できるその他の意味のある失敗のインジケーターを渡すことによって、それらが発生しないようにします。例:ヌル引数の例外。
厄介なこと:あなたが所有していないコードがあなたに投げかけている比較的単純なエラー。通常、自分の骨頭の例外に対処するのと同じ方法で、これらすべてをキャッチして対処する必要があります。二度と捨てないでください。例:C#のInt32.Parse()メソッドからのフォーマット例外
外因性: Vexing(他の人のコードから)またはBoneheaded (コードから)の状況によく似ているが、現実にはそれらをスローしているコードが実際に回復する方法を知らないことを示しているため、スローする必要がある比較的単純なエラー発信者はおそらくそうするでしょう。先に進んでこれらをスローしますが、コードが他の場所からそれらを受け取ったら、それらをキャッチして処理します。例:ファイルが見つからないという例外。
4つのうち、外因性のものは、正しくするために最も考えなければならないものです。ファイルが見つからないことを示す例外は、IOライブラリメソッドにスローするのに適しています。特に、状況がいつでも発生する可能性があり、ファイルが見つからない場合、メソッドはファイルが見つからない場合に何をすべきかをほぼ確実に認識しません。状況が一時的であるかどうかを検出する方法はありません。ただし、このような例外をスローすることは、アプリケーションレベルのコードには適切ではありません。これは、そのアプリケーションがユーザーから続行方法に関する情報を取得できるためです。
デストラクタから例外をスローしないでください。
オブジェクトの状態に関するいくつかの基本的なレベルの例外保証を維持します。
本当に例外エラーであり、上位層にそれを知らせたい場合を除いて、エラーコードを使用して実行できるエラーを伝達するために例外を使用しないでください。
あなたがそれを助けることができるならば、例外を投げないでください。それはすべてを遅くします。
ただしcatch(...)
て何もしないでください。知っている例外または特定の例外をキャッチします。少なくとも、何が起こったかをログに記録します。
例外の世界では、安全なものがなくなったため、RAIIを使用します。
出荷コードは、少なくともメモリに関して例外を抑制すべきではありませんでした。
例外をスローするときは、上位層がそれらをデバッグするのに十分な情報を持つように、可能な限り多くの情報を一緒にパッケージ化します。
STLなどのライブラリが不明な動作(無効なイテレータ/ベクトル添え字オーバーフローなど)を示す代わりに例外をスローする原因となる可能性のあるフラグについて知ってください。
例外オブジェクトのコピーの代わりに参照をキャッチしますか?
例外をスローする可能性のあるコードを操作する場合は、COMなどの参照カウントオブジェクトに特に注意し、参照カウントポインタでワープします。
コードが2%を超える時間で例外をスローする場合は、パフォーマンスのためにエラーコードにすることを検討してください。
一部のコンパイラは、Cコードが例外をスローしないと想定して最適化するため、装飾されていないdllエクスポート/Cインターフェイスから例外をスローしないことを検討してください。
例外を処理するために行うことすべてが以下のようなものである場合は、例外処理をまったく使用しないでください。あなたはそれを必要としません。
main
{
try {
all code....
}
catch(...) {}
}
例外は処理時間がかかるため、アプリで実際に発生してはならないことが発生した場合にのみスローする必要があります。
場合によっては、どのようなことが起こるかを予測し、それらから回復するためのコードを作成できます。その場合は、例外をスローしてキャッチし、ログに記録して回復してから続行するのが適切です。それ以外の場合は、デバッグに役立つようにできるだけ多くの情報をキャプチャしながら、予期しないものを処理して正常に終了するために使用する必要があります。
私は .NET 開発者です。キャッチ アンド スローの場合、私のアプローチは次のとおりです。
この回答が与えられているコンテキストは、Java 言語です。
ポップアップする可能性のある通常のエラーについては、それらを直接処理します (何かが null、空などの場合にすぐに戻るなど)。例外的な状況に対してのみ、実際の例外を使用します。
ただし、チェック例外をスローすることはありません。独自の特定の例外に対して RuntimeException をサブクラス化し、該当する場合は直接キャッチします。また、他のライブラリや JDK API などによってスローされる例外については、内部で試行/キャッチし、例外をログに記録します (本当にすべきことが起こった場合)。バッチ ジョブのファイルが見つからない例外のように回復する方法がない場合)、または例外を RuntimeException にラップしてからスローします。コードの外側では、JVM であれ Web コンテナーであれ、最終的にその RuntimeException をキャッチする例外ハンドラーに依存しています。
これが行われる理由は、メソッドを呼び出すインスタンスが 4 つある可能性があるにもかかわらず、実際に例外を処理できるのは 1 つだけである場合に、強制的な try/catch ブロックを作成することを避けるためです。これは、(しゃれは意図されていません... 痛い) 例外ではなく、ルールであるように思われるため、4 番目の例外がそれを処理できる場合でも、例外をキャッチして、例外の根本原因を調べて、発生した実際の例外を取得できます ( RuntimeException ラッパーについて心配する必要はありません)。
通常、リソースへのアクセス、データの整合性、データの有効性に基づいて例外を判断する良い方法があると思います。
アクセス例外
データの完全性
データの有効性
明らかに他のケースもありますが、これらは通常、必要に応じて従おうとするものです。
例外処理に関する私のポリシーは次の場所にあります。
http://henko.net/imperfection/exception-handling-policy-throwing-exception/。
(Webサイトを宣伝することは規則に違反しないことを願っていますが、ここに貼り付けるには少し情報が多すぎます。)
他の人はこれを修正/明確化する必要があるかもしれませんが、(私が信じている)「コントラクト駆動型開発」と呼ばれる戦略があり、パブリック インターフェイスで各メソッドに期待される前提条件と保証された事後条件を明示的に文書化します。次に、メソッドを実装するときに、コントラクトの事後条件を満たさないエラーが発生すると、例外がスローされます。前提条件を満たさない場合、プログラム エラーと見なされ、プログラムが中止されます。
コントラクト駆動型の開発が例外のキャッチの問題について話しているかどうかはわかりませんが、一般的には、予期して合理的に回復できる例外のみをキャッチする必要があります。たとえば、ほとんどのコードは Out Of Memory 例外から有意に回復できないため、これをキャッチしても意味がありません。一方、書き込み用にファイルを開こうとしている場合は、ファイルが別のプロセスによって排他的にロックされている場合、またはファイルが削除されている場合を処理できます (また、処理する必要があります)。開こうとする前の存在)。
別のコメンターが指摘したように、例外を使用して、予想および回避できる予想される条件を処理することも避ける必要があります。たとえば、.NET フレームワークでは、int.TryParse は、特にループなどで使用される場合に、try/catch を使用する int.Parse よりも適しています。
bea(現在はオラクル)のこの記事は、それについての良い説明です:http://www.oracle.com/technology/pub/articles/dev2arch/2006/11/effective-exceptions.html。Javaを前提としていますが、他の環境でも使用できるはずです。
C++ 開発者としての私自身のポリシーは、クラス/モジュールに対するパブリック API と見なされるものから例外をスローしないことです (実際、COM の要件です)。ただし、プライベート クラスの実装では例外を広く使用しています。たとえば、ATL を使用すると、次のようになります。
HRESULT Foo()
{
HRESULT hr = S_OK;
try {
// Avoid a whole lot of nested ifs and return code
// checking - internal stuff just throws.
DoStuff();
DoMoreStuff(); // etc.
} catch ( CAtlException& e ) {
hr = e;
}
return hr;
}
void DoSomething()
{
// If something goes wrong, AtlThrow( E_FAILED or E_WHATEVER );
}
仕様に従って言語環境によって発生する例外ではありません。実際に例外の概念がある場合、使用されている言語の 私はJavaで「ゼロ除算」、またはAdaでCONSTRAINT_ERRORを考えているのに対し、Cでは何も考えていません。
構成内に例外が定義されているプログラミング言語を選択した後、プログラマーはどのようにして例外を使用することを「決定」できますか?
編集:または、例外を「使用する」のではなく、例外の「処理」に関するまとまりのある一貫したポリシーをいつ持つべきかということですか?
Edit2: Steven Dewhurst の著書「C++ Gotchas」の無料の章、具体的には Gotcha 64 と Gotcha 65 を参照してください。C++ に焦点を当てていますが、関連するレッスンは他の言語でも役立ちます。