10

重複の可能性:
再スローによる不適切なスタックトレース

一般に、.NETthrow;ではスタック トレースはリセットされませんが、リセットされることが認められてthrow ex;います。

ただし、この単純なプログラムでは、異なる行番号を取得します。

void Main()
{
    try
    {
        try
        {
            Wrapper(); // line 13
        }
        catch(Exception e)
        {
            Console.WriteLine(e.ToString());
            throw; // line 18
        }
    }
    catch(Exception e)
    {
          Console.WriteLine(e.ToString());
    }
}

public void Wrapper()
{
    Throw(); // line 28
}

public void Throw()
{
    var x = (string)(object)1; // line 33
}

出力は次のとおりです。

System.InvalidCastException: タイプ 'System.Int32' のオブジェクトをタイプ 'System.String' にキャストできません。C:\long-path\Program.cs:line 13 の ConsoleApplication2.Program.Main(String[] args) で

System.InvalidCastException: タイプ 'System.Int32' のオブジェクトをタイプ 'System.String' にキャストできません。C:\long-path\Program.cs:line 18 の ConsoleApplication2.Program.Main(String[] args) で

注: 最初のスタック トレースには 13 行目が含まれ、2 つ目のスタック トレースには 18 行目が含まれます。さらに、13 行目も 18 行目も実際にキャストが行われる行ではありません。

私の質問は次のとおりです。どのような状況でスタック トレースが変更され、どの状況throw;でスタック トレースが変更されないのでしょうか。

これはすでに観察されていますが、一般的には回答されていないことに注意してください。


更新:
上記のコードをデバッグ モードで実行したところ、次の結果が得られました。

System.InvalidCastException: タイプ 'System.Int32' のオブジェクトをタイプ 'System.String' にキャストできません。C:\long-path\Program.cs:line 33 の ConsoleApplication2.Program.Throw() C:\long-path\Program.cs:Line 28 の ConsoleApplication2.Program.Wrapper() ConsoleApplication2.Program.Main (String[] args) in C:\long-path\Program.cs:line 13

System.InvalidCastException: タイプ 'System.Int32' のオブジェクトをタイプ 'System.String' にキャストできません。C:\long-path\Program.cs:line 33 の ConsoleApplication2.Program.Throw() C:\long-path\Program.cs:Line 28 の ConsoleApplication2.Program.Wrapper() ConsoleApplication2.Program.Main (String[] args) in C:\long-path\Program.cs:line 18

注意: 最後の行番号はまだ変更されています

4

2 に答える 2

5

これが発生する理由は、Release モードで実行中のメソッドのインライン化のためです。WrapperメソッドとThrowメソッドを Release モードでインライン化したくない場合は、次の[MethodImpl]属性でそれらを装飾できます。

[MethodImpl(MethodImplOptions.NoInlining)]
public void Wrapper()
{
    Throw();
}

[MethodImpl(MethodImplOptions.NoInlining)]
public void Throw()
{
    var x = (string)(object)1;
}
于 2012-09-11T15:57:40.447 に答える
4

Darin がすでに指摘しているように、スタック トレースの削減はメソッドのインライン化によるものです。ただし、スタックトレースで利用できるラインリファレンスが均等でない点もあります。

この背後にある説明はわかりませんが、例外を再スローするときにすべてのスタック トレース情報を保持する方法があります。新しい例外をスローし、キャッチした例外を内部例外として渡す必要があります。このアプローチでは、マージされたスタックトレースには、例外の起点と、例外が再スローされたポイントが含まれます。

私はこれについて話し、次のブログ投稿で例外を再スローするさまざまな方法の完全な例を示しました。

.NET 例外 – throw ex は悪ですが、throw はそれほど無害ではありません


あなたのコメントは私に簡単な調査の動機を与えましたが、私が見つけることができた最高のものは、 catch and rethrow に関するブログ投稿での Jonathan de Halleux による次のコメントでした。

また、再スローするメソッドのスタックトレースの行番号も変更します (再スローがそのメソッドのスローの場所になるため)。

これはさらに詳しく説明できますが、おそらく、行情報を取得するために使用されるスロー サイトが各メソッドで追跡され、再スローによってオーバーライドされるという事実を示しています。

throwそのため、元のスロー サイトの代わりに使用するとスタック トレースが保持されthrow eますが、ラップして新しい例外をスローしない限り、失われます。

その他の試すべきこと; SO はダイレクト メッセージを許可しておらず、上記のコメントはPeliによって作成されたものであるため、この質問に でタグ付けしpexて、彼の注意を引き、そのコメントをフォローアップしてもらうことができます。:)

于 2012-09-11T16:09:42.050 に答える