22

Javaでは、finallyブロックを実行する前に例外が発生したかどうかを検出するための洗練された方法はありますか?「close()」ステートメントを処理する場合、finallyブロック内で例外処理が必要になるのが一般的です。理想的には、両方の例外を維持し、それらを伝播する必要があります(両方に有用な情報が含まれている可能性があるため)。これを行うために私が考えることができる唯一の方法は、try-catch-finallyスコープの外に変数を設定して、スローされた例外への参照を保存することです。次に、「保存された」例外を、finallyブロックで発生する例外とともに伝播します。

これを行うためのよりエレガントな方法はありますか?おそらくこれを明らかにするAPI呼び出しですか?

これが私が話していることのいくつかの大まかなコードです:

Throwable t = null; 
try {   
   stream.write(buffer); 
} catch(IOException e) {
    t = e;   //Need to save this exception for finally
    throw e;
} finally {   
    try {
       stream.close();   //may throw exception
   } catch(IOException e) {
      //Is there something better than saving the exception from the exception block?
      if(t!=null) {
         //propagate the read exception as the "cause"--not great, but you see what I mean.
         throw new IOException("Could not close in finally block: " + e.getMessage(),t);
      } else {
         throw e;  //just pass it up
      }    
   }//end close
}

明らかに、例外をメンバー変数として保存したり、メソッドから返したりするなど、他にも同様の問題がいくつかありますが、もう少しエレガントなものを探しています。

多分何かのようなThread.getPendingException()ものか似たようなものですか?さらに言えば、他の言語でエレガントな解決策はありますか?

この質問は、興味深い質問を提起した別の質問のコメントから実際に生まれました。

4

5 に答える 5

12

try/catch/finally の範囲外で変数を設定するというあなたの考えは正しいです。

一度に複数の例外が伝播することはありません。

于 2008-10-08T20:25:41.097 に答える
6

Boolean フラグを使用する代わりに、Exception オブジェクトへの参照を格納します。そうすれば、例外が発生したかどうかを確認する方法があるだけでなく (例外が発生しなかった場合、オブジェクトは null になります)、例外が発生した場合は、finally ブロック内の例外オブジェクト自体にもアクセスできます。すべての catch ブロックでエラー オブジェクトを設定することを忘れないでください (エラーを再スローする場合)。

これは、C# 言語に欠けている機能であり、追加する必要があると思います。 finally ブロックは、catch ブロックがサポートする方法と同様に、ベース Exception クラスへの参照をサポートする必要があります。これにより、伝播する例外への参照が finally ブロックで使用できるようになります。これはコンパイラーにとって簡単なタスクでありローカルの Exception 変数を手動作成し、エラーを再スローする前にその値を手動で設定することを忘れないください。エラーを再スローしません (finally ブロックに表示したいのは、キャッチされていない例外だけであることを思い出してください)。

finally (Exception main_exception)
{
    try
    {
        //cleanup that may throw an error (absolutely unpredictably)
    }
    catch (Exception err)
    {
        //Instead of throwing another error,
        //just add data to main exception mentioning that an error occurred in the finally block!
        main_exception.Data.Add( "finally_error", err );
        //main exception propagates from finally block normally, with additional data
    }
}

上記のように... finally ブロックで例外を利用できるようにしたい理由は、finally ブロックが独自の例外をキャッチした場合、新しいエラーをスローしてメインの例外を上書きする代わりに (悪い)または単にエラーを無視する (これも悪い)と、元のエラーに追加データとしてエラーが追加される可能性があります。

于 2009-08-30T19:23:03.887 に答える
2

キャッチにはいつでもブールフラグを設定できます。私はそれを行うための「巧妙な」方法を知りませんが、それなら私は.Netの人です。

于 2008-10-08T20:23:20.353 に答える
2

ロギングを使用...

try {   
   stream.write(buffer); 
} catch(IOException ex) {
    if (LOG.isErrorEnabled()) { // You can use log level whatever you want
        LOG.error("Something wrong: " + ex.getMessage(), ex);
    }
    throw ex;
} finally {   
    if (stream != null) {
        try {
            stream.close();
        } catch (IOException ex) {
            if (LOG.isWarnEnabled()) {
                LOG.warn("Could not close in finally block", ex);
            }
        }
    }
}
于 2008-10-10T09:21:12.357 に答える
1

vb.net では、"Catch...When" ステートメントを使用して、実際にキャッチしなくてもローカル変数の例外を取得できます。これには多くの利点があります。その中で:

  1. 例外を「最終的に」キャッチするものがない場合は、元の例外の場所から未処理の例外トラップが発生します。特に、デバッグに必要な情報がまだ範囲外に出たり、'finally' ステートメントによって掃引されたりしていないため、最後の再スローでデバッガー トラップを使用するよりもはるかに優れています。
  2. 再スローは、"Throw Ex" のようにスタック トレースをクリアしませんが、スタック トレースをジンクスすることがよくあります。例外がキャッチされない場合、スタック トレースはクリーンになります。

この機能は vb ではサポートされていないため、vb ラッパーを記述して C でコードを実装すると役立つ場合があります (たとえば、MethodInvoker と Action(Of Exception) が与えられた場合、"Try" 内で MethodInvoker を実行し、"ついに"。

興味深い癖が 1 つあります。Catch-When が例外を検出し、Finally 句の例外によって上書きされる可能性があります。場合によっては、これが良いこともあります。それ以外の場合は、混乱を招く可能性があります。いずれにせよ、それは知っておくべきことです。

于 2010-12-21T18:32:20.890 に答える