回答:
スタックトレースを保持する方法は、throw;
これも有効です。
try {
// something that bombs here
} catch (Exception ex)
{
throw;
}
throw ex;
基本的には、その時点から例外をスローするようなものなので、スタックトレースはthrow ex;
ステートメントを発行している場所にしか行きません。
Mikeも正解です。例外により例外を渡すことができると想定しています(推奨)。
カール・セガンは持っている例外処理に大きな書き込みを彼に電子書籍をプログラミングの基礎偉大な読み取りであるだけでなく。
編集:プログラミングの基礎 pdf へのリンク。テキストで「例外」を検索してください。
ExceptionDispatchInfo.Capture(ex).Throw(); throw;
.NET 4.5でstackoverflow.com/questions/57383/...
初期例外とともに新しい例外をスローすると、初期スタックトレースも保持されます。
try{
}
catch(Exception ex){
throw new MoreDescriptiveException("here is what was happening", ex);
}
AggregateException
集約された操作の例外にのみ使用してください。たとえば、CLRのParallelEnumerable
およびTask
クラスによってスローされます。使用法はおそらくこの例に従うべきです。
実際、throw
ステートメントがStackTrace情報を保持しない状況がいくつかあります。たとえば、次のコードでは:
try
{
int i = 0;
int j = 12 / i; // Line 47
int k = j + 1;
}
catch
{
// do something
// ...
throw; // Line 54
}
StackTraceは、行54で例外が発生したが、行47で例外が発生したことを示します。
Unhandled Exception: System.DivideByZeroException: Attempted to divide by zero.
at Program.WithThrowIncomplete() in Program.cs:line 54
at Program.Main(String[] args) in Program.cs:line 106
上記のような状況では、元のStackTraceを保持するための2つのオプションがあります。
Exception.InternalPreserveStackTraceの呼び出し
これはプライベートメソッドであるため、リフレクションを使用して呼び出す必要があります。
private static void PreserveStackTrace(Exception exception)
{
MethodInfo preserveStackTrace = typeof(Exception).GetMethod("InternalPreserveStackTrace",
BindingFlags.Instance | BindingFlags.NonPublic);
preserveStackTrace.Invoke(exception, null);
}
StackTrace情報を保存するためにプライベートメソッドに依存するという欠点があります。.NET Frameworkの将来のバージョンで変更される可能性があります。上記のコード例と提案された解決策は、Fabrice MARGUERIEのウェブログから抽出されました。
Exception.SetObjectDataの呼び出し
以下の手法は、Anton TykhyyによってC#の回答として提案されました。スタックトレースの質問を失うことなく、InnerExceptionを再スローする方法を教えてください。
static void PreserveStackTrace (Exception e)
{
var ctx = new StreamingContext (StreamingContextStates.CrossAppDomain) ;
var mgr = new ObjectManager (null, ctx) ;
var si = new SerializationInfo (e.GetType (), new FormatterConverter ()) ;
e.GetObjectData (si, ctx) ;
mgr.RegisterObject (e, 1, si) ; // prepare for SetObjectData
mgr.DoFixups () ; // ObjectManager calls SetObjectData
// voila, e is unmodified save for _remoteStackTraceString
}
ただし、パブリックメソッドのみに依存するという利点がありますが、これは次の例外コンストラクタにも依存します(サードパーティによって開発された一部の例外は実装していません)。
protected Exception(
SerializationInfo info,
StreamingContext context
)
私の状況では、使用しているサードパーティライブラリによって発生した例外がこのコンストラクターを実装していないため、最初のアプローチを選択する必要がありました。
するとthrow ex
、基本的に新しい例外がスローされ、元のスタックトレース情報を見逃してしまいます。 throw
推奨される方法です。
経験則では、基本Exception
オブジェクトのキャッチとスローを回避します。これにより、例外について少し賢くなります。言い換えるSqlException
と、処理コードがで問題を起こさないように、aを明示的にキャッチする必要がありますNullReferenceException
。
ただし、現実の世界では、ベース例外をキャッチしてログに記録することもお勧めしますが、すべてを調べて、例外を取得することを忘れないでくださいInnerExceptions
。
常に「スロー」を使用する必要があります。.NETで例外を再スローするには、
これを参照してください、 http://weblogs.asp.net/bhouse/archive/2004/11/30/272297.aspx
基本的に、MSIL(CIL)には、「スロー」と「再スロー」の2つの指示があります。
基本的に、「throw ex」がスタックトレースをオーバーライドする理由を確認できます。
throw ex;
がJavaで再スローすると思われる理由の可能性のある犯人も記しています-Javaではそうです!しかし、Aグレードの回答を得るために、ここにその情報を含める必要があります。(私はまだExceptionDispatchInfo.Capture
jeuoekdcwzfwccuからの回答に追いついていますが)
ExceptionDispatchInfo.Capture( ex ).Throw()
とプレーンの違いを説明した人はいませんthrow
。ただし、の問題に気づいた人もいthrow
ます。
キャッチされた例外を再スローする完全な方法は、使用することですExceptionDispatchInfo.Capture( ex ).Throw()
(.Net 4.5からのみ利用可能)。
以下に、これをテストするために必要なケースがあります。
1。
void CallingMethod()
{
//try
{
throw new Exception( "TEST" );
}
//catch
{
// throw;
}
}
2。
void CallingMethod()
{
try
{
throw new Exception( "TEST" );
}
catch( Exception ex )
{
ExceptionDispatchInfo.Capture( ex ).Throw();
throw; // So the compiler doesn't complain about methods which don't either return or throw.
}
}
3。
void CallingMethod()
{
try
{
throw new Exception( "TEST" );
}
catch
{
throw;
}
}
4。
void CallingMethod()
{
try
{
throw new Exception( "TEST" );
}
catch( Exception ex )
{
throw new Exception( "RETHROW", ex );
}
}
ケース1とケース2は、CallingMethod
メソッドのソースコード行番号が行の行番号であるスタックトレースを提供しますthrow new Exception( "TEST" )
。
ただし、ケース3では、CallingMethod
メソッドのソースコード行番号がthrow
呼び出しの行番号であるスタックトレースが得られます。これは、throw new Exception( "TEST" )
行が他の操作に囲まれている場合、例外が実際にスローされた行番号がわからないことを意味します。
ケース4は、元の例外の行番号が保持されるという点でケース2と似ていますが、元の例外のタイプを変更するため、実際の再スローではありません。
throw ex;
にします。これがそれらすべての最良の答えです。
「throw」と「throw ex」は同じことをするかもしれないが、例外が発生した行である重要な情報を提供しない、非常に重要なポイントを実際に見逃した人もいます。
次のコードを検討してください。
static void Main(string[] args)
{
try
{
TestMe();
}
catch (Exception ex)
{
string ss = ex.ToString();
}
}
static void TestMe()
{
try
{
//here's some code that will generate an exception - line #17
}
catch (Exception ex)
{
//throw new ApplicationException(ex.ToString());
throw ex; // line# 22
}
}
「throw」または「throw ex」のいずれかを実行すると、スタックトレースが表示されますが、line#は#22になるため、例外をスローしている行を正確に特定できません(1つまたはいくつかしかない場合を除く)。 tryブロックのコード行)。例外の期待される行#17を取得するには、元の例外スタックトレースで新しい例外をスローする必要があります。
私は間違いなく使用します:
try
{
//some code
}
catch
{
//you should totally do something here, but feel free to rethrow
//if you need to send the exception up the stack.
throw;
}
それはあなたのスタックを保持します。
throw
。たとえば、使い捨てをクリーンアップして(エラー時にのみ呼び出す場合)、例外をスローすることができます。
参考までに、これと「throw」によって報告されたスタックトレースをテストしました。完全に正しいスタックトレースではありません。例:
private void foo()
{
try
{
bar(3);
bar(2);
bar(1);
bar(0);
}
catch(DivideByZeroException)
{
//log message and rethrow...
throw;
}
}
private void bar(int b)
{
int a = 1;
int c = a/b; // Generate divide by zero exception.
}
スタックトレースは例外の発生元を正しく指し示しますが(報告された行番号)、foo()に対して報告された行番号はスローの行です。ステートメントなので、bar()への呼び出しのどれが例外を引き起こしたかはわかりません。