19

重複:C#で、スタックトレースを失うことなくInnerExceptionを再スローするにはどうすればよいですか?

バックグラウンドスレッドで非同期的に呼び出す操作がいくつかあります。時々、物事は悪くなります。これが発生すると、TargetInvocationExceptionが発生する傾向がありますが、これは適切ではありますが、まったく役に立ちません。私が本当に必要としているのは、次のようなTargetInvocationExceptionのInnerExceptionです。

    try
    {
        ReturnValue = myFunctionCall.Invoke(Target, Parameters);
    }
    catch (TargetInvocationException err)
    {
        throw err.InnerException;
    }

そうすれば、発生したREAL例外が発信者に提供されます。問題は、throwステートメントがスタックトレースをリセットしているように見えることです。基本的に内部例外を再スローしたいのですが、元々持っていたスタックトレースを保持します。それ、どうやったら出来るの?

明確化: 内部例外のみが必要な理由は、このクラスが、これらの関数(呼び出し元によって提供されたデリゲート)が他のスレッドなどで実行されているという事実全体を「抽象化」しようとするためです。例外がある場合、それはバックグラウンドスレッドで実行されることとは何の関係もない可能性があり、呼び出し元は、呼び出しの呼び出しではなく、デリゲートに入り、実際の問題を見つけるスタックトレースを本当に望んでいます。

4

8 に答える 8

29

反射せずに再スローする前に、スタックトレースを保持することができます。

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
}

これは、InternalPreserveStackTraceと比較して多くのサイクルを浪費しますが、パブリック機能のみに依存するという利点があります。スタックトレース保存機能の一般的な使用パターンを次に示します。

// usage (A): cross-thread invoke, messaging, custom task schedulers etc.
catch (Exception e)
{
    PreserveStackTrace (e) ;

    // store exception to be re-thrown later,
    // possibly in a different thread
    operationResult.Exception = e ;
}

// usage (B): after calling MethodInfo.Invoke() and the like
catch (TargetInvocationException tiex)
{
    PreserveStackTrace (tiex.InnerException) ;

    // unwrap TargetInvocationException, so that typed catch clauses 
    // in library/3rd-party code can work correctly;
    // new stack trace is appended to existing one
    throw tiex.InnerException ;
}
于 2010-01-18T10:39:59.790 に答える
6

いいえ、それは不可能です。唯一の本当の機会は、推奨されるパターンに従い、適切なを使用して独自の例外をスローすることInnerExceptionです。

編集

の存在が懸念され、TargetInvocationExceptionそれを無視したい場合(別のスレッドで実行されているという事実と関係がある可能性があるため、これをお勧めしません)、自分のスレッドをスローすることを妨げるものは何もありませんここでの例外とあなた自身InnerExceptionのものとしてからを添付します。少し臭いですが、やりたいことができるかもしれません。TargetInvocationExceptionInnerException

于 2009-06-17T22:08:32.293 に答える
5

リモーティングを使用するときにサーバー側のスタックトレースを保持するために使用される内部メカニズムを使用して、例外でスタックトレースを「リセット」する方法がありますが、それは恐ろしいことです。

try
{
    // some code that throws an exception...
}
catch (Exception exception)
{
    FieldInfo remoteStackTraceString = typeof(Exception).GetField("_remoteStackTraceString", BindingFlags.Instance | BindingFlags.NonPublic);
    remoteStackTraceString.SetValue(exception, exception.StackTrace);
    throw exception;
}

これにより、元のスタックトレースが例外の_remoteStackTraceStringフィールドに配置され、例外が再スローされると、新しくリセットされたスタックトレースに連結されます。

これは本当に恐ろしいハックですが、それはあなたが望むものを達成します。ただし、クラス内をいじくり回しているSystem.Exceptionため、このメソッドはフレームワークの後続のリリースで機能しなくなる可能性があります。

于 2009-06-17T22:44:12.207 に答える
2

TargetInvocationExceptionは「役に立たない」と感じるかもしれませんが、それは現実です。.NETが元の例外を受け取らなかったふりをして、TargetInvocationExceptionでラップしてスローしないようにしてください。それは本当に起こりました。いつの日か、TargetInvocationExceptionをスローしたコードの場所など、そのラッピングから取得した情報が必要になる場合もあります。

于 2009-06-17T22:11:26.140 に答える
0

他の人が言っているように、例外チェーンを無傷に保つために、「throw」キーワードを追加せずに使用します。元の例外が必要な場合(それが意味していると仮定して)、チェーンの最後でException.GetBaseException()を呼び出して、すべてを開始した例外を取得できます。

于 2009-06-18T13:33:01.807 に答える
0

あなたはそれをすることはできません。throwパラメータなしで使用されない限り、常にスタックトレースをリセットします。発信者はInnerExceptionを使用する必要があります...

于 2009-06-17T22:10:33.460 に答える
0

例外を指定して「throw」キーワードを使用すると、常にスタックトレースがリセットされます。

最善の方法は、必要な実際の例外をキャッチし、「throw」を使用することです。「throwex;」の代わりに。または、渡したいInnerExceptionを使用して、独自の例外をスローします。

あなたがやりたいことが可能だとは思いません。

于 2009-06-17T22:10:58.123 に答える
0

.net4.5で可能です。

catch(Exception e)
{
   ExceptionDispatchInfo.Capture(e.InnerException).Throw();
}
于 2017-06-07T07:29:22.313 に答える