2011-03-01 7 views
23

मैं हमेशा "फेंक" और "फेंक पूर्व" was that throw alone wasn't resetting the stacktrace of the exception.क्या एक callstack पुनर्स्थापित करने के लिए

दुर्भाग्य के बीच का अंतर सोचा है (मैं "फेंक", नहीं "फेंक पूर्व" उपयोग कर रहा हूँ) फेंक नेतृत्व कर सकते हैं, कि है मैं जिस व्यवहार का अनुभव कर रहा हूं वह नहीं; यहां एक साधारण नमूना है जो मेरी समस्या का पुनरुत्पादन करता है:

using System; 
using System.Text; 

namespace testthrow2 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      try 
      { 
       try 
       { 
        throw new Exception("line 14"); 
       } 
       catch (Exception) 
       { 
        throw; // line 18 
       } 
      } 
      catch (Exception ex) 
      { 
       Console.WriteLine(ex.ToString()); 

      } 
      Console.ReadLine(); 
     } 
    } 
} 

मैं इस कोड को लाइन 14 पर शुरू होने वाले कॉलस्टैक को प्रिंट करने की अपेक्षा करता हूं; हालांकि कॉलस्टैक 18 वें स्थान पर शुरू होता है। बेशक यह नमूना में कोई बड़ा सौदा नहीं है, लेकिन मेरे वास्तविक जीवन आवेदन में, प्रारंभिक त्रुटि जानकारी खोना काफी दर्दनाक है।

क्या मुझे कुछ याद आ रही है? वहाँ (यानी ढेर जानकारी खोने के बिना एक अपवाद फेंक फिर से?)

मैं .net का उपयोग कर रहा एक और तरीका प्राप्त करने के लिए जो मैं चाहता 3.5

उत्तर

32

आप इस लेख पढ़ना चाहिए:

संक्षेप में, throwआमतौर पर मूल फेंका अपवाद के स्टैक ट्रेस को बरकरार रखता है, लेकिन एकमात्र अपवाद में नहीं होती है, तो वर्तमान ढेर फ्रेम (यानी विधि)।

एक विधि PreserveStackTrace (कि ब्लॉग लेख में दिखाया गया है) नहीं है कि आपको लगता है कि इस तरह मूल स्टैक ट्रेस को बरकरार रखता है का उपयोग करें:

try 
{ 

} 
catch (Exception ex) 
{ 
    PreserveStackTrace(ex); 
    throw; 
} 

लेकिन मेरी हमेशा की तरह समाधान या तो बस पकड़ नहीं करने के लिए और फेंक अपवाद फिर है

try 
{ 

} 
catch (Exception ex) 
{ 
    throw new Exception("Error doing foo", ex); 
} 
+0

धन्यवाद, आपके द्वारा प्रदान किया गया लिंक वास्तव में उपयोगी था, और सीधे InternalPreserveStackTrace को कॉल करने से मुझे मेरी समस्या हल करने की अनुमति मिली! – Brann

+1

यदि उपर्युक्त लिंक कभी मर जाता है, या कोई इसका पालन नहीं करना चाहता है, तो आपको अपने अपवाद पर स्टैक को संरक्षित करने के लिए केवल इतना करना है: 'टाइपऑफ (अपवाद) .GetMethod ("InternalPreserveStackTrace", बाइंडिंगफ्लैग। इंस्टेंस | बाइंडिंगफ्लैग नॉनपब्लिक) .वॉवोक (पूर्व, शून्य); ' –

8

समस्या यह है कि विंडोज ढेर के शुरुआती बिंदु को रीसेट किया जाता है है। सीएलआर अपेक्षित व्यवहार कर रहा है-यह होस्ट ऑपरेटिंग सिस्टम के अपवाद हैंडलिंग समर्थन की केवल एक सीमा है। समस्या यह है कि प्रति विधि कॉल केवल एक स्टैक फ्रेम हो सकता है।

आप अपने अपवाद हैंडलिंग दिनचर्या को एक अलग "सहायक" विधि में निकाल सकते हैं, जो विंडोज के एसईएच द्वारा लगाई गई सीमाओं के आसपास काम करेगा, लेकिन मुझे नहीं लगता कि यह एक अच्छा विचार है।

उचित तरह से ढेर जानकारी खोने के बिना एक अपवाद rethrow करने के लिए एक नई अपवाद फेंक और भीतरी अपवाद के रूप में मूल, पकड़ा अपवाद शामिल करने के लिए है।

बहुत सारे मामलों की कल्पना करना मुश्किल है जहां आप वास्तव में ऐसा करने की आवश्यकता है। यदि आप को अपवाद नहीं रखते हैं, और बस इसे फिर से निकालने के लिए इसे पकड़ते हैं, तो शायद आपको इसे पहले स्थान पर नहीं पकड़ना चाहिए।

+0

ठीक है; तो "फेंक" और "पूर्व फेंक" के बीच क्या अंतर है? – Brann

+0

उदाहरण के लिए यह प्रश्न देखें http://stackoverflow.com/questions/730250/is-there-a-difference-between-throw-and-throw-ex; यह आपके उत्तर – Brann

+0

@ ब्रैन के साथ विरोधाभास में प्रतीत होता है: उह, वे अभी भी अलग हैं। 'फेंक दें' स्टैक ट्रेस रीसेट करता है, 'फेंक' नहीं करता है। जिस समस्या को आपने यहां स्थापित किया है वह एक बढ़त मामला है। आपके पास एक ही विधि में अपवाद हैंडलर हैं, और विंडोज केवल एक विधि कॉल प्रति स्टैक फ्रेम का समर्थन करता है। 'फेंक 'और' फेंक पूर्व 'के बीच का अंतर सीएलआर के स्तर पर है; जैसा कि मेरे उत्तर में बताया गया है, यह ठीक से यहां काम कर रहा है। आप मेजबान ऑपरेटिंग सिस्टम की सीमा के खिलाफ बस मार रहे हैं। –

2

सामान्य rethrow स्टैक ट्रेस exc पर सब कुछ को बरकरार रखता है: इस तरह (जब तक बिल्कुल जरूरी), या बस हमेशा नए अपवाद मूल अपवाद प्रचार करने के लिए InnerException संपत्ति का उपयोग कर फेंक ept कि अगर वर्तमान विधि स्टैक ट्रेस में है, तो लाइन नंबर अधिलेखित हो जाएगा। यह कष्टप्रद व्यवहार है।सी # में एक असाधारण मामले में कुछ करने की जरूरत है, लेकिन परवाह नहीं करता क्या अपवाद नहीं है अगर, एक पैटर्न का उपयोग कर सकते हैं:

 
    Boolean ok = False; 
    try 
    { 
    do_something(); 
    ok = True; 
    } 
    finally 
    { 
    if (!ok) // An exception occurred! 
     handle_exception(); 
    } 

एक संख्या जहां उस पैटर्न बहुत उपयोगी है कर रहे हैं; सबसे आम एक ऐसा कार्य होगा जो एक नया आईडीस्पोजेबल लौटाएगा। यदि समारोह वापस नहीं जा रहा है, तो डिस्पोजेबल ऑब्जेक्ट को साफ करना होगा। ध्यान दें कि उपर्युक्त "कोशिश" ब्लॉक के भीतर कोई भी "वापसी" कथन सही पर ठीक सेट होना चाहिए।

 
    Dim PendingException As Exception = Nothing; 
    Try 
    Do_Something 
    PendingException = Nothing ' See note 
    Catch Ex As Exception When CopyFirstParameterToSecondAndReturnFalse(Ex, PendingException) 
    Throw ' Will never execute, since above will return false 
    Finally 
    If PendingException IsNot Nothing Then 
     .. Handle exception 
    EndIf 
    End Try 

लंबे समय से नामित समारोह को:

vb.net में, यह पैटर्न के साथ, एक पैटर्न जो कार्यात्मक रूप से एक छोटे से अच्छे है उपयोग करने के लिए हालांकि कोड में एक जगह एक छोटे से भावुक है, संभव है स्पष्ट फैशन में लागू किया जाना चाहिए। इस पैटर्न को अपवाद को कोड में उपलब्ध कराने का लाभ है। यद्यपि इसे अक्सर संभाल-लेकिन-पकड़ने की स्थितियों में जरूरी नहीं है, वहां एक ऐसी स्थिति है जहां यह अमूल्य हो सकती है: यदि एक सफाई दिनचर्या अपवाद फेंकता है। आम तौर पर, यदि एक क्लीनअप दिनचर्या अपवाद फेंकता है, तो कोई लंबित अपवाद खो जाएगा। उपर्युक्त पैटर्न के साथ, हालांकि, सफाई अपवाद के भीतर लंबित अपवाद को लपेटना संभव है।

उपर्युक्त कोड के साथ एक दिलचस्प नोट: "कैच कब" तक पहुंचने के लिए अपवाद के लिए संभव है और फिर भी सामान्य रूप से पूरा करने के लिए प्रयास कथन के लिए संभव है। यह वास्तव में स्पष्ट नहीं है कि उस परिस्थिति में क्या होना चाहिए, लेकिन स्पष्ट है कि एक बात यह है कि अंत में कथन कार्य नहीं करना चाहिए, हालांकि अपवाद लंबित है। समाशोधन लंबित अपवाद यह सुनिश्चित करेगा कि यदि कोई अपवाद गायब हो जाता है, तो कोड व्यवहार करेगा जैसा कि यह कभी नहीं हुआ। एक विकल्प अपवाद को अपनाने और पुनर्स्थापित करने के लिए किया जाएगा, क्योंकि यह स्थिति लगभग निश्चित रूप से आंतरिक अपवाद-हैंडलिंग कोड के साथ कुछ गलत इंगित करती है।

+0

मेरा पहला विचार आपके पहले उदाहरण जैसा ही था, लेकिन ऐसी स्थिति की कल्पना करना बहुत मुश्किल है जहां आपके पास ऐसी वस्तु है जो 'आईडीस्पोजेबल' लागू करती है और आप नहीं कर सके बस पूरी चीज को 'उपयोग' ब्लॉक में लपेटें। यहां कई अन्य प्रश्न हैं जो दर्शाते हैं कि 'उपयोग' ब्लॉक के * अंदर * से लौटने में कुछ भी गलत नहीं है। जहां तक ​​आपका दूसरा उदाहरण है, ब्लीच! यह निश्चित रूप से "चालाक" लगता है, लेकिन यह उन चीजों में से एक है जो अपने स्वयं के अच्छे के लिए बहुत दूर * चालाक है। मैं कल्पना नहीं कर सकता कि इस तरह के कोड को बनाए रखने के लिए मजबूर होना चाहिए। इसे आंतरिक पूर्व के रूप में लपेटना बहुत स्पष्ट है। –

+0

@ कोडी ग्रे: एक "उपयोग" ब्लॉक में एक कन्स्ट्रक्टर को उपयोगी रूप से कैसे लपेटता है? यदि ऑब्जेक्ट कॉलर पर वापस किए बिना दायरे से बाहर जा रहा है, तो इसे निपटान किया जाना चाहिए, लेकिन यदि ऑब्जेक्ट कॉलर पर वापस लौटने जा रहा है, तो यह नहीं होना चाहिए। एक "प्रयोग" ब्लॉक ऐसे परिदृश्यों के साथ कोई सहायता प्रदान नहीं करता है। – supercat

+0

@ कोडी ग्रे: लंबित अपवाद को पकड़ने के लिए icky कोड के लिए, मेरी इच्छा है कि कुछ बेहतर भाषा निर्माण हो, लेकिन ऐसा नहीं है। यदि कोड के एक क्षेत्र को क्लीनअप की आवश्यकता होगी तो अपवाद फेंकता है, यह असामान्य नहीं है कि अपवाद का कारण होने वाली स्थिति से सफाई विफल हो जाएगी। एक असफल सफाई से अपवादों को निगलना बुरा है, लेकिन क्लीनअप कोड को एक नया अपवाद फेंकना भी बुरा है जो पहले के किसी को नष्ट कर देता है। सबसे अच्छा अपवाद के रूप में पुराने अपवाद के साथ अपवाद फेंकने के लिए क्लीनअप के लिए सबसे अच्छा है, लेकिन * एक ऐसा कैसे करता है जो कोड के बिना मैंने दिखाया है? – supercat

संबंधित मुद्दे

 संबंधित मुद्दे