13

私は、catchブロックにいるときに、「throw;」を使用して現在の例外を再スローできることを読みました。または「exを投げる;」。

差出人:http ://msdn.microsoft.com/en-us/library/ms182363%28VS.80%29.aspx

「例外を除いて元のスタックトレース情報を保持するには、例外を指定せずにthrowステートメントを使用してください。」

しかし、私がこれを試してみると

        try{
            try{
                try{
                    throw new Exception("test"); // 13
                }catch (Exception ex1){
                    Console.WriteLine(ex1.ToString());
                    throw; // 16
                }
            }catch (Exception ex2){
                Console.WriteLine(ex2.ToString()); // expected same stack trace
                throw ex2; // 20
            }
        }catch (Exception ex3){
            Console.WriteLine(ex3.ToString());
        }

私は3つの異なるスタックを取得します。私は最初と2番目のトレースが同じであることを期待していました。私は何が間違っているのですか?(または間違った理解?)

System.Exception:c:\ Program.cs:line 13のConsoleApplication1.Program.Main(String [] args)でテストします。System.Exception:c:\ ProgramのConsoleApplication1.Program.Main(String [] args)でテストします。 cs:line 16 System.Exception:c:\ Program.cs:line 20のConsoleApplication1.Program.Main(String [] args)でテストします。

4

3 に答える 3

7

throw現在のフレーム内からスタックフレームをスローしない場合にのみ、スタックフレームが保持されます。これらすべてを1つの方法で実行することで、まさにそれを実行できます。

この回答を参照してください:https ://stackoverflow.com/a/5154318/1517578

PS:実際に有効な質問であった質問をするための+1。

于 2013-02-04T04:04:46.800 に答える
2

サイモンは私に答えてくれましたが、別の関数の内部から元の例外をスローするだけで、期待される動作を確認できます。

static void Main(string[] args)
{
    try
    {
        try
        {
            try
            {
                Foo();
            }
            catch (Exception ex1)
            {
                Console.WriteLine(ex1.ToString());
                throw;
            }
        }
        catch (Exception ex2)
        {
            Console.WriteLine(ex2.ToString()); // expected same stack trace
            throw ex2;
        }
    }
    catch (Exception ex3)
    {
        Console.WriteLine(ex3.ToString());
    }
}

static void Foo()
{
    throw new Exception("Test2");
}
于 2013-02-04T04:10:29.483 に答える
2

さて、私はこの問題についてもう少し掘り下げました、そしてここに私の非常に個人的な結論があります:

「投げる」は絶対に使用しないでください。ただし、常に原因を指定して新しい例外を再スローします。

これが私の推論です:

私は以前の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

これらすべてに対する私の単純な結論は、「投げる」を決して使用しないことです。ただし、原因を指定して常に新しい例外を再スローします。

于 2013-02-05T03:11:27.007 に答える