私はこれについての議論を見てきました。入れ子になった試行錯誤で、大丈夫であることがわかります。その時までに外側の試みはすでにキャッチを引き起こしており、したがってスコープの問題はありません。逆に....エラーを隠します(スコープの問題です)。
try {
try{
Throws exception;
} catch (exception innerE) {
outerE.printStackTrace(); /* I know the error , printing but have no way of passing what I know to the outer try*/
}
}
catch (exception outerE){
outerE.printStackTrace(); /* Going to override the inner and not say anything because the inner catch is not visible to my scope */
}
これはどうしてアンチパターンではないのですか?
このパターンには適切な使用法があります(不適切な使用法があるため)。コメントで述べたように、内部情報には追加情報を含めることができます。
try
{
try
{
throw new GenericException("Generic message");
}
catch (Exception innerEx)
{
// rollback some changes perhaps
throw new MoreDetailedException(innerEx, "More details supplied here as well as by the type of exception");
}
}
catch (Exception outerEx)
{
Console.WriteLine(outerEx);
}
これを使用して、内部ループで回復できる処理エラーをキャッチし、処理を続けることもできます。
try
{
try
{
_response = ProcessInputFromUser();
}
catch (Exception innerEx)
{
_response = 0;
}
ProcessResponse(_response);
}
catch (Exception outerEx)
{
Console.WriteLine(outerEx);
}
これを処理する不適切な方法は、エラーを2回記録し、キャッチして単純に再スローするなどです。
try
{
try
{
}
catch (Exception innerEx)
{
Console.WriteLine(innerEx);
throw;
}
}
catch (Exception outerEx)
{
Console.WriteLine(outerEx);
}
これと、try-catchを使用する別のメソッドから呼び出されるtry-catchを含むメソッドを作成することには、それほど大きな違いはありません。これはユビキタスであり、(それ自体では)問題ありません。可能な場合は、ネストされたtry-catchを独自のメソッドにリファクタリングする傾向があります。
このようなことをしたい場合の例は、ソケットへの書き込みのようなものです。ソケットの切断が発生した場合は、おそらく再接続して再試行するだけです(上限まで)。ただし、簡単に回復できない別のエラーが発生した場合は、別の方法で処理する必要があります。
繰り返しになりますが、読みやすさの観点から、ネストされた部分を独自のメソッドに移動するのが一般的に最善です。しかし、これは主に表面的な変更なので、アンチパターンになるかどうかはわかりません。
Try/catch句はかなり複雑であるため、技術的にはtry/catchブロックをネストすることは技術的に一般的ですが、通常は別のメソッドにあります。ネストされたtry/catchブロックを実行する一般的な場所は次のとおりです。
注:コード例は擬似コードです
一括解析の場合、次のようなループを作成できます。
_try {
foreach(var file in listOfFiles) {
parseFile(file);
}
}
catch (Exception e) {
log.error("Stopped parsing all files due to unexpected error", e);
notifyUser("Stopped parsing files");
}
_
そしてparseFile()
の中に別のtry/catchがあります:
_private void parseFile(string fileName) throws ParseException, IOException {
var lineNo = 0;
using (var input = new TextReader(File.OpenRead(filename))) {
foreach (var line in input.ReadLines()) {
lineNo++;
try {
parseRecord(line);
} catch (Exception e) {
throw new ParseException(fileName, lineNo, e);
}
}
}
}
_
これが通常の構造である場合、このパターンは行の各フィールドに対して繰り返されることさえあります。つまり、IOExceptionがどのようにバブルアップするか、およびファイルの解析中の内部例外をラップして、パーサーのデバッグを容易にする追加のコンテキストを提供できることがわかります。
Catch句をさらに高度化すると、その例外をログに記録してレコードをスキップするか、ファイルの処理を完全に停止するかを決定できます。
結論として、ネストされたtry/catchブロックを処理するのには非常に十分な理由があります。これは、すべてのネストされたtry/catchブロックが適切であるという意味ではありません。他のエラー処理と同様に、エラーが適切かつ予測可能な方法で処理されていることを確認する必要があります。
(コメントを回答に変換)
このセクションでは、ネストされたtry-catchブロックについて一般的に言及しています。
一般に、複数のアクションを実行する関数またはコードの一部であり、エラー処理/アプリケーションの状態のクリーンアップが、アクション/コードのどこで失敗したかによって異なる場合は、複数のtry-catchブロックを使用する必要があります。
他の回答が指摘したように、最良のコーディングスタイルは、これらの内部try-catchブロックを独自のメソッドに抽出し、これらのメソッドに、それらが「試行している」ことを説明する適切な名前を付けます。より複雑なコードでは、メソッド名もより複雑になります。実行するクリーンアップについて説明する必要があります。
このセクションでは、コードサンプルをそのまま参照します。
OPはネストされたtry-catchがどのように機能するかについて誤解していると思います。
最も内側のコードブロックがスローすると、内部のキャッチによってキャッチされます。内部のcatchブロックがスロー/再スローせず、誤って別の例外がスローされない範囲で、外部のcatchは何もキャッチしません。
コードサンプルが過度に簡略化されている-rethrow
がないか、または何かが省略されている(または元のコードに実際に欠けている)ため、OPが想定しているコードの理解が妨げられています。
したがって、アウターキャッチをトリガーする可能性のある状況(可能性は低いですが)があるかどうかを特定するために、アウターキャッチブロックの意味を慎重に調査(調査)する必要があります。
これは、コードにコメントを残す正当なケースです。このような分析は多くの場合時間がかかるため、自明ではありません。
また、アウターキャッチがトリガーされる状況はまれである可能性があります(一般的なプログラマーがそのような可能性を認識していない可能性もあります)が、課題追跡で順位を上げるのに十分な頻度で発生した可能性があります。プログラマーが状況を再現する方法を知ることができるように、コメントはこの可能性を指摘して問題追跡トラッカーを指す必要があります。将来のコード変更がこのまれな状況を正しく処理し続けることをテストするためです。