16

throw exthrowの違いを理解すると、元の StackTrace がこの例で保持されるのはなぜですか。

    static void Main(string[] args)
    {
        try
        {
            LongFaultyMethod();
        }
        catch (System.Exception ex)
        {
            Console.WriteLine(ex.StackTrace);
        }
    }

    static void LongFaultyMethod()
    {
        try
        {
            int x = 20;
            SomethingThatThrowsException(x);
        }
        catch (Exception)
        {
            throw;
        }
    }

    static void SomethingThatThrowsException(int x)
    {
        int y = x / (x - x);
    }

しかし、これではありません:

    static void Main(string[] args)
    {
        try
        {
            LongFaultyMethod();
        }
        catch (System.Exception ex)
        {
            Console.WriteLine(ex.StackTrace);
        }
    }

    static void LongFaultyMethod()
    {
        try
        {
            int x = 20;
            int y = x / (x - 20);
        }
        catch (Exception)
        {
            throw;
        }
    }

2 番目のシナリオは、 throw exと同じ出力を生成していますか?

どちらの場合も、y が初期化された行番号が表示されることが期待されます。

4

2 に答える 2

18

この制限が C# 言語、CLI、またはこれらの Microsoft 実装内にあるかどうかはわかりませんが、2 番目の例はException.InternalPreserveStackTrace、次の投稿に記載されているように、への明示的な呼び出しが必要な場合です。このメソッドはinternalであるため、通常はリフレクションを通じて呼び出す必要があります。これに伴うパフォーマンスの問題はAction<Exception>、この回答の最後に示されているように、 for を作成することでほぼ完全に軽減できます。

参照:例外の再スローと完全なコール スタック トレースの保持

編集: ECMA-335 パーティション I §12.4.2 (例外処理) およびパーティション III §4.24 (再スロー) を再検討した後、あなたが見ている動作は CLR (Microsoft の CLI の実装) のセマンティック エラーであると考えています。動作に関する唯一の特定の参照は、「Arethrowはオブジェクトのスタック トレースを変更しません」です。ここで説明されているケースでは、再スローは実際にはスタック トレースを変更しており、PreserveStackTraceハックを既知の CLR 欠陥の回避策にしています。

static void LongFaultyMethod() 
{ 
    try 
    { 
        int x = 20; 
        int y = x / (x - 20); 
    } 
    catch (Exception ex) 
    { 
        PreserveStackTrace(ex); // <-- add this line
        throw; 
    } 
} 

PreserveStackTraceこれは、そのブログエントリの最適化です。

private static readonly Action<Exception> _internalPreserveStackTrace =
    (Action<Exception>)Delegate.CreateDelegate(
        typeof(Action<Exception>),
        typeof(Exception).GetMethod(
            "InternalPreserveStackTrace",
            BindingFlags.Instance | BindingFlags.NonPublic));

public static void PreserveStackTrace(Exception e)
{
    _internalPreserveStackTrace(e);
}
于 2010-04-04T17:55:23.367 に答える
2

2 番目の例では、同じメソッドから例外を再スローしているためです。最初に別のメソッドからスローされたのはそのためです。1 つのメソッド スコープでは、スタック トレースは 1 つだけです。

次のように実行します。最善の方法は、例外の深さを確認できるように、例外を常に新しい例外内にラップすることです。

「同じメソッドで再スローが発行された場合 (例外スタック トレースにはメソッドごとに 1 つの行番号情報しかありません。メソッド A の行番号 2 で例外がスローされ、同じメソッド A で例外がスローされたというスタック トレースは表示されません。行番号 17 から再スローされた場合、例外が再スローされた最後の行番号のみが含まれます。」

try        
{            
   int x = 20;            
   int y = x / (x - 20);        
}        
catch (Exception ex)        
{            
   // do something here.. like log or something
   throw new Exception("Internal Exception", ex);        
}

私のコメントを読まないコメントがあまりにも多くて驚いています!! おそらくこれを安全にログに記録する必要があるとコメントに書きました。さまざまな理由があります。最上位のコードが例外を消費し、どの例外がどこでスローされたかわからない場合、ログは例外を交差させるのに役立ちます!!!

ログに記録する必要がない場合は、例外をキャッチしないでください。

于 2009-10-18T15:02:28.697 に答える