さて、私はこの問題についてもう少し掘り下げました、そしてここに私の非常に個人的な結論があります:
「投げる」は絶対に使用しないでください。ただし、常に原因を指定して新しい例外を再スローします。
これが私の推論です:
私は以前のJavaの経験に影響を受け、C#のスローはJavaのスローと非常に似ていると予想していました。さて、私はこの問題についてもう少し掘り下げました、そしてここに私の観察があります:
static void Main(string[] args){
try {
try {
throw new Exception("test"); // 13
}
catch (Exception ex) {
Console.WriteLine(ex.ToString());
throw ex;// 17
}
} catch (Exception ex) {
Console.WriteLine(ex.ToString());
}
}
収量:
System.Exception: test
at ConsoleApplication1.Program.Main(String[] args) in Program.cs:line 13
System.Exception: test
at ConsoleApplication1.Program.Main(String[] args) in Program.cs:line 17
直感的には、Javaプログラマーは両方の例外が同じであると予想していました。
System.Exception: test
at ConsoleApplication1.Program.Main(String[] args) in Program.cs:line 13
しかし、C#のドキュメントには、これが予想されることであると明確に記載されています。
「throwステートメントで例外を指定して例外が再スローされると、スタックトレースが現在のメソッドで再開され、例外をスローした元のメソッドと現在のメソッドの間のメソッド呼び出しのリストが失われます。例外を除いて元のスタックトレース情報を保持するには、例外を指定せずにthrowステートメントを使用します。」</ p>
ここで、テストを少し変更した場合(throw ex; by throw; 17行目)。
try {
try {
throw new Exception("test"); // 13
}
catch (Exception ex) {
Console.WriteLine(ex.ToString());
throw;// 17
}
} catch (Exception ex) {
Console.WriteLine(ex.ToString());
}
収量:
System.Exception: test
at ConsoleApplication1.Program.Main(String[] args) in Program.cs:line 13
System.Exception: test
at ConsoleApplication1.Program.Main(String[] args) in Program.cs:line 17
明らかに、これは私が期待したものではありません(これは元の質問だったので)。2番目のスタックトレースで元のスローポイントを失いました。サイモンホワイトヘッドは説明をリンクしました、それはそのスローです。現在のメソッドで例外が発生しなかった場合にのみ、スタックトレースを保持します。したがって、同じメソッドでパラメーターを指定せずに「スロー」することは、一般に、例外の原因を見つけるのに役立たないため、まったく役に立ちません。
Javaプログラマーが行うことを実行して、17行目のステートメントを次のように置き換えました。
throw new Exception("rethrow", ex);// 17
収量:
System.Exception: test
at ConsoleApplication1.Program.Main(String[] args) in Program.cs:line 13
System.Exception: rethrow ---> System.Exception: test
at ConsoleApplication1.Program.Main(String[] args) in Program.cs:line 13
--- End of inner exception stack trace ---
at ConsoleApplication1.Program.Main(String[] args) in Program.cs:line 17
これははるかに良い結果です。
次に、メソッド呼び出しでテストを開始しました。
private static void throwIt() {
throw new Exception("Test"); // 10
}
private static void rethrow(){
try{
throwIt(); // 15
} catch (Exception ex) {
Console.WriteLine(ex.ToString());
throw; // 18
}
}
static void Main(string[] args){
try{
rethrow(); // 24
} catch (Exception ex) {
Console.WriteLine(ex.ToString());
}
}
繰り返しますが、スタックトレースは、私(Javaプログラマー)が期待したものではありませんでした。Javaでは、両方のスタックは同じであり、3つのメソッドの深さは次のとおりです。
java.lang.Exception: Test
at com.example.Test.throwIt(Test.java:10)
at com.example.Test.rethrow(Test.java:15)
at com.example.Test.main(Test.java:24)
最初のスタックトレースは、2メソッドの深さしかありません。
System.Exception: Test
at ConsoleApplication1.Program.throwIt() in Program.cs:line 10
at ConsoleApplication1.Program.rethrow() in Program.cs:line 15
これは、スタックの巻き戻しプロセスの一部としてスタックトレースにデータが入力されているようなものです。その時点で例外を調査するためにスタックトレースをログに記録するとしたら、おそらく重要な情報が欠落しているでしょう。
2番目のスタックトレースは3メソッドの深さですが、18行目(throw;)が表示されます。
System.Exception: Test
at ConsoleApplication1.Program.throwIt() in Program.cs:line 10
at ConsoleApplication1.Program.rethrow() in Program.cs:line 18
at ConsoleApplication1.Program.Main(String[] args) in Program.cs:line 24
この観察結果は、以前に行った観察結果と似ています。現在のメソッドスコープではスタックトレースが保持されないため、例外が発生した呼び出されたメソッドが失われます。たとえば、rethowが次のように記述されている場合:
private static void rethrow(){
try{
if (test)
throwIt(); // 15
else
throwIt(); // 17
} catch (Exception ex) {
Console.WriteLine(ex.ToString());
throw; // 20
}
}
収量
System.Exception: Test
at ConsoleApplication1.Program.throwIt() in Program.cs:line 10
at ConsoleApplication1.Program.rethrow() in Program.cs:line 20
at ConsoleApplication1.Program.Main(String[] args) in Program.cs:line 26
throwIt()のどの呼び出しが例外をスローしましたか?スタックは20行目を示しているので、15行目ですか17行目ですか?
解決策は前と同じです。原因を新しい例外でラップして、次のようにします。
System.Exception: rethrow ---> System.Exception: Test
at ConsoleApplication1.Program.throwIt() in Program.cs:line 10
at ConsoleApplication1.Program.rethrow(Boolean test) in Program.cs:line 17
--- End of inner exception stack trace ---
at ConsoleApplication1.Program.rethrow(Boolean test) in Program.cs:line 20
at ConsoleApplication1.Program.Main(String[] args) in Program.cs:line 26
これらすべてに対する私の単純な結論は、「投げる」を決して使用しないことです。ただし、原因を指定して常に新しい例外を再スローします。