383. Preserving the stack trace of an exception

ראינו בטיפ מספר 65 מהי הדרך הנכונה לזרוק Exception שתפסנו.

ראינו שקיים הבדל בין throw; לthrow ex;.

במקרים ממש נדירים, לא נתפוס Exception, אלא נקבל אותו כערך מפונקציה/מקום אחר. במקרים כאלה כאשר נזרוק את הException היינו מעוניינים לשמר את הStack trace המקורי של הException.

לדוגמה, Castle כתבו ספריה שנקראת Castle Windsor Wcf Facilities.

אחד הדברים שהם עושים שם בין השאר זה להחליף את הProxy הסטנדרטי שמגיע עם Wcf (שממומש ע”י RealProxy – ראו גם טיפ מספר 348) במימוש שלהם ע”י מימוש בזמן ריצה את הממשק של הServiceContract. (ראו גם טיפים 349, 347)

הסיבה שהם עשו את זה, היא בגלל שכפי שראינו בעבר, שימוש בניתוב של הRealProxy הוא הרבה יותר איטי ממחלקה רגילה.

איך הם עשו זאת? הם מחזיקים את הProxy הרגיל בתור Member וקוראים לפונקציית הניתוב שלו.

פונקציית הניתוב מחזירה מדי פעם Exception, אבל בתור פרמטר של הReturnMessage. הבעיה בזה היא שאם זורקים את הException בצורה נאיבית ע”י throw message.Exception;, יעלם הStack Trace המקורי.

Castle מצאו איזשהו פתרון נחמד.

מסתבר שיש במחלקה Exception איזושהי מתודה בשם InternalPreserveStackTrace שאם נקרא לה, אז הStackTrace ישמר.

הבעיה – המתודה היא internal.

ככה נראה הפתרון שלהם:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public static class ExceptionHelper
{
private static readonly MethodInfo PreserveStackTraceMethod =
typeof(Exception).GetMethod("InternalPreserveStackTrace",
BindingFlags.Instance |
BindingFlags.NonPublic);
public static Exception PreserveStackTrace(Exception exception)
{
if (PreserveStackTraceMethod != null && exception != null)
{
PreserveStackTraceMethod.Invoke(exception, null);
}
return exception;
}
}

הם פשוט קוראים למתודה בReflection 😃


בגדול זה צורך שהוא לא כל כך נפוץ, ומן הסתם זה די Bad Practice לקרוא למתודה שהיא Private בReflection.

עם זאת, מעניין לראות שיש תמיכה בדבר כזה. אם נסתכל בReflector על המימוש של המתודה, נראה שהוא בסה"כ מאתחל איזשהו שדה של הException שנקרא _remoteStackTraceString.

ע"י טיפה יותר מחקר, ניתן להבין שזהו שדה שהמציאו בRemoting על מנת להעביר את הStack Trace של Exception שקפץ בצד Server לClient. לא הצלחתי למצוא שימוש במתודה InternalPreserveStackTrace בReflector, אבל כנראה Remoting או dll אחר של הFramework משתמש בזה.

המשך יום משמר עקבות של מחסנית טוב.

שתף