كيف يمكنني rethrow الداخلية استثناء مع الحفاظ على تتبع المكدس ولدت حتى الآن ؟

StackOverflow https://stackoverflow.com/questions/1009762

سؤال

مكررة من: في C#, كيف يمكنني rethrow InnerException دون أن تفقد تتبع مكدس?

لدي بعض العمليات التي لا تحتج بشكل متزامن على مؤشر ترابط خلفية.أحيانا تسير الأمور بشكل سيء.عندما يحدث هذا, أنا أميل إلى الحصول على TargetInvocationException ، والتي ، في الوقت المناسب ، هو عديمة الفائدة تماما.ما أريده حقا هو TargetInvocationException هو InnerException, مثل هذا:

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

هذا الطريق ، المتصلين هم خدم مع استثناء الحقيقي التي حدثت.المشكلة هي أن رمي البيان يبدو أن إعادة تتبع المكدس.أود أن الأساس rethrow الاستثناء الداخلي ، ولكن الحفاظ على تتبع المكدس أنه كان في الأصل.كيف أفعل ذلك ؟

التوضيح: السبب في أنني أريد فقط الاستثناء الداخلي هو أن هذه الفئة يحاول المجرد بعيدا' كله حقيقة أن هذه الوظائف (المندوبين توفيره من قبل المتصل) يتم تشغيل مؤشرات الترابط الأخرى وغيرها.إذا كان هناك استثناء ، ثم هي احتمالات أنها لا علاقة لها مع كونها تعمل على خلفية الموضوع, و المتصل أود حقا تتبع المكدس الذي يذهب إلى مندوب ويجد المشكلة الحقيقية ليست دعوة إلى الاحتجاج.

هل كانت مفيدة؟

المحلول

ذلك هو ممكن للحفاظ على تتبع المكدس قبل rethrowing دون تفكير:

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 ;
}

نصائح أخرى

لا, هذا غير ممكن.لديك فقط فرصة حقيقية هو اتباع نمط أوصى ورمي الخاصة بك استثناء مع المناسبة InnerException.

تحرير

إذا كان القلق هو وجود TargetInvocationException و كنت ترغب في تجاهل ذلك (لا أوصي هذا ، كما أنه قد جيد جدا لها أن تفعل شيئا مع حقيقة أن يتم تشغيل مؤشر ترابط آخر) ثم لا شيء يمنعك من رمي الخاص بك الاستثناء هنا ربط InnerException من TargetInvocationException كما الخاصة بك InnerException.انها قليلا من رائحة كريهة ، ولكن قد تنجز ما تريد.

هناك طريقة "إعادة" تتبع المكدس على استثناء باستخدام الآلية الداخلية التي يتم استخدامها للحفاظ على جانب الملقم تتبعات المكدس عند استخدام الاتصال عن بعد ، لكنه الرهيبة:

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 الدرجة على الرغم من ذلك هذه الطريقة ولذلك قد كسر في الإصدارات اللاحقة من الإطار.

على الرغم من أنك قد تشعر أن TargetInvocationException هو "عديمة الفائدة" ، هذه هي الحقيقة.لا تحاول أن تتظاهر .صافي لم تأخذ استثناء الأصلي والتفاف مع TargetInvocationException ورميها.هذا حدث فعلا.يوما ما, ربما كنت تريد بعض قطعة من المعلومات التي تأتي من أن التفاف - ربما مثل موقع التعليمات البرمجية التي رمى TargetInvocationException.

لا يمكنك أن تفعل ذلك. throw يعيد دائما تتبع المكدس ، ما لم تستخدم من دون المعلمة.أخشى المتصلين بك سوف تضطر إلى استخدام InnerException...

باستخدام "رمي" الكلمة مع استثناء دائما إعادة تتبع المكدس.

أفضل شيء نفعله هو أن القبض الفعلي استثناء تريد استخدام "رمي;" بدلا من "رمي السابقين؛".أو رمي الخاص بك استثناء ، مع InnerException التي تريد أن يمر على طول.

أنا لا أصدق ما تريد القيام به هو ممكن.

كما قال آخرون ، واستخدام "رمي" الكلمة دون إضافة للحفاظ على استثناء سلسلة سليمة.إذا كنت بحاجة إلى أن الأصل استثناء (على افتراض أن هذا هو ما تقصد) ثم يمكنك الاتصال استثناء.GetBaseException() في نهاية سلسلة الخاص بك للحصول على الاستثناء الذي بدأ كل شيء.

فمن الممكن مع .net 4.5:

catch(Exception e)
{
   ExceptionDispatchInfo.Capture(e.InnerException).Throw();
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top