345

リフレクションを通じて、例外を引き起こす可能性のあるメソッドを呼び出しています。ラッパーのリフレクションを配置せずに、呼び出し元に例外を渡すにはどうすればよいですか?
InnerException を再スローしていますが、これによりスタック トレースが破棄されます。
コード例:

public void test1()
{
    // Throw an exception for testing purposes
    throw new ArgumentException("test1");
}

void test2()
{
    try
    {
        MethodInfo mi = typeof(Program).GetMethod("test1");
        mi.Invoke(this, null);
    }
    catch (TargetInvocationException tiex)
    {
        // Throw the new exception
        throw tiex.InnerException;
    }
}
4

10 に答える 10

542

.NET 4.5 では、クラスがExceptionDispatchInfo追加されました。

これにより、例外をキャプチャして、スタック トレースを変更せずに再スローできます。

using ExceptionDispatchInfo = 
    System.Runtime.ExceptionServices.ExceptionDispatchInfo;

try
{
    task.Wait();
}
catch(AggregateException ex)
{
    ExceptionDispatchInfo.Capture(ex.InnerException).Throw();
}

これは、だけでなく、すべての例外で機能しますAggregateException

これは、非同期言語機能を同期言語機能に近づけるために、インスタンスawaitから内部例外をアンラップする C# 言語機能のために導入されました。AggregateException

于 2013-06-13T15:42:46.940 に答える
87

リフレクションなしで再スローする前にスタック トレースを保持することが可能です。

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:42:22.577 に答える
35

これをcatchブロックに入れるのが最善の策だと思います:

throw;

そして、後で内部例外を抽出します。

于 2008-09-11T19:20:00.400 に答える
14
public static class ExceptionHelper
{
    private static Action<Exception> _preserveInternalException;

    static ExceptionHelper()
    {
        MethodInfo preserveStackTrace = typeof( Exception ).GetMethod( "InternalPreserveStackTrace", BindingFlags.Instance | BindingFlags.NonPublic );
        _preserveInternalException = (Action<Exception>)Delegate.CreateDelegate( typeof( Action<Exception> ), preserveStackTrace );            
    }

    public static void PreserveStackTrace( this Exception ex )
    {
        _preserveInternalException( ex );
    }
}

例外をスローする前に、例外で拡張メソッドを呼び出します。元のスタック トレースが保持されます。

于 2009-11-02T20:39:15.773 に答える
10

さらに反省…

catch (TargetInvocationException tiex)
{
    // Get the _remoteStackTraceString of the Exception class
    FieldInfo remoteStackTraceString = typeof(Exception)
        .GetField("_remoteStackTraceString",
            BindingFlags.Instance | BindingFlags.NonPublic); // MS.Net

    if (remoteStackTraceString == null)
        remoteStackTraceString = typeof(Exception)
        .GetField("remote_stack_trace",
            BindingFlags.Instance | BindingFlags.NonPublic); // Mono

    // Set the InnerException._remoteStackTraceString
    // to the current InnerException.StackTrace
    remoteStackTraceString.SetValue(tiex.InnerException,
        tiex.InnerException.StackTrace + Environment.NewLine);

    // Throw the new exception
    throw tiex.InnerException;
}

プライベート フィールドは API の一部ではないため、これはいつでも壊れる可能性があることに注意してください。Mono bugzillaの詳細な議論を参照してください。

于 2008-09-11T19:20:25.967 に答える
10

まず、TargetInvocationException を失わないでください。これは、デバッグを行う場合に役立つ情報です。
2 番目: TIE を InnerException として独自の例外タイプにラップし、必要なものにリンクする OriginalException プロパティを配置します (コールスタック全体をそのまま保持します)。
3 番目: TIE がメソッドから出てくるようにします。

于 2008-09-11T19:22:57.770 に答える
5

みんな、あなたはかっこいい..私はすぐにネクロマンサーになるつもりです。

    public void test1()
    {
        // Throw an exception for testing purposes
        throw new ArgumentException("test1");
    }

    void test2()
    {
            MethodInfo mi = typeof(Program).GetMethod("test1");
            ((Action)Delegate.CreateDelegate(typeof(Action), mi))();

    }
于 2010-01-02T17:48:10.210 に答える
3

例外のシリアライゼーション/デシリアライゼーションを使用する別のサンプル コード。実際の例外タイプがシリアライズ可能である必要はありません。また、パブリック/保護されたメソッドのみを使用します。

    static void PreserveStackTrace(Exception e)
    {
        var ctx = new StreamingContext(StreamingContextStates.CrossAppDomain);
        var si = new SerializationInfo(typeof(Exception), new FormatterConverter());
        var ctor = typeof(Exception).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { typeof(SerializationInfo), typeof(StreamingContext) }, null);

        e.GetObjectData(si, ctx);
        ctor.Invoke(e, new object[] { si, ctx });
    }
于 2012-04-03T08:19:24.807 に答える