.NET अपवादों को पकड़ने और फिर से फेंकने के लिए सर्वोत्तम अभ्यास


284

अपवादों को पकड़ने और उन्हें फिर से फेंकने पर विचार करने के लिए सबसे अच्छे अभ्यास क्या हैं? मैं यह सुनिश्चित करना चाहता हूं कि Exceptionऑब्जेक्ट का InnerExceptionऔर स्टैक ट्रेस संरक्षित रहे। क्या वे इसे संभालने के तरीके में निम्नलिखित कोड ब्लॉक के बीच कोई अंतर है?

try
{
    //some code
}
catch (Exception ex)
{
    throw ex;
}

बनाम:

try
{
    //some code
}
catch
{
    throw;
}

जवाबों:


262

स्टैक ट्रेस को संरक्षित करने का तरीका इस के उपयोग के माध्यम से है और throw;साथ ही मान्य है

try {
  // something that bombs here
} catch (Exception ex)
{
    throw;
}

throw ex;मूल रूप से उस बिंदु से एक अपवाद को फेंकने की तरह है, इसलिए स्टैक ट्रेस केवल उस स्थान पर जाएगा जहां आप throw ex;बयान जारी कर रहे हैं ।

माइक भी सही है, यह मानते हुए कि अपवाद आपको अपवाद (जो अनुशंसित है) पास करने की अनुमति देता है।

कार्ल सेगिन ने प्रोग्रामिंग ई-बुक की अपनी नींव में एक अपवाद के रूप में अच्छी तरह से लिखा है , जो एक महान पढ़ा है।

संपादित करें: प्रोग्रामिंग पीडीएफ की नींव के लिए काम कर रहे लिंक । बस "अपवाद" के लिए पाठ खोजें।


10
मुझे यकीन नहीं है कि अगर यह लिखना अद्भुत है, तो यह सुझाव देता है कि {// ...} कैच (एक्सेप्शन पूर्व) {थ्रो न्यू एक्ससेप्शन (उदा। मिसेज + "अन्य सामान"); } अच्छा है। समस्या यह है कि आप उस अपवाद को संभालने में पूरी तरह से असमर्थ हैं, जब तक कि आप सभी अपवादों को नहीं पकड़ लेते, एक बड़ा नो-नो (आपको यकीन है कि आप उस
आउटऑफ़मैरी एक्ससेप्शन

2
@ljs आपकी टिप्पणी के बाद से लेख बदल गया है क्योंकि मुझे ऐसा कोई खंड नहीं दिखता जहाँ वह अनुशंसा करता है। वास्तव में इसके विपरीत, वह ऐसा नहीं करने के लिए कहता है और पूछता है कि क्या आप आउटऑफमेरीरीसेप्शन को अच्छी तरह से संभालना चाहते हैं !?
रायनफेस्कॉटलैंड

6
कभी-कभी फेंक देते हैं; स्टैक ट्रेस को संरक्षित करने के लिए पर्याप्त नहीं है। यहाँ एक उदाहरण है https://dotnetfiddle.net/CkMFoX
Artavazd Balayan

4
या ExceptionDispatchInfo.Capture(ex).Throw(); throw;.NET में +4.5 stackoverflow.com/questions/57383/…
अल्फ्रेड वालेस

@AlfredWallace समाधान ने मेरे लिए पूरी तरह से काम किया। {...} पकड़ {फेंक} का प्रयास करें स्टैक ट्रेस को संरक्षित नहीं किया। धन्यवाद।
एटॉनसन

100

यदि आप प्रारंभिक अपवाद के साथ एक नया अपवाद फेंकते हैं तो आप प्रारंभिक स्टैक ट्रेस को भी संरक्षित करेंगे।

try{
} 
catch(Exception ex){
     throw new MoreDescriptiveException("here is what was happening", ex);
}

कोई फर्क नहीं पड़ता कि मैं क्या नया अपवाद फेंकने की कोशिश करता हूं ("संदेश", पूर्व) हमेशा पूर्व फेंकता है और कस्टम संदेश की उपेक्षा करता है। हालांकि, नया अपवाद ("संदेश", ex.InnerException) काम करता है।
टॉड

अगर कोई कस्टम अपवाद की आवश्यकता नहीं है, तो एक AggregateException (.NET 4+) का उपयोग कर सकते हैं। msdn.microsoft.com/en-us/library/…
निकोस सोकोस

AggregateExceptionकेवल समेकित संचालन पर अपवादों के लिए उपयोग किया जाना चाहिए। उदाहरण के लिए, इसे CLR की कक्षाओं द्वारा ParallelEnumerableऔर फेंक दिया जाता है Task। उपयोग को संभवतः इस उदाहरण का पालन करना चाहिए।
अलुआन हदाद

29

दरअसल, कुछ स्थितियां ऐसी होती हैं, जो throwस्टैकट्रेस जानकारी को संरक्षित नहीं करती हैं। उदाहरण के लिए, नीचे दिए गए कोड में:

try
{
  int i = 0;
  int j = 12 / i; // Line 47
  int k = j + 1;
}
catch
{
  // do something
  // ...
  throw; // Line 54
}

स्टैकट्रेस संकेत देगा कि रेखा 54 ने अपवाद को उठाया, हालांकि इसे 47 की रेखा पर उठाया गया था।

Unhandled Exception: System.DivideByZeroException: Attempted to divide by zero.
   at Program.WithThrowIncomplete() in Program.cs:line 54
   at Program.Main(String[] args) in Program.cs:line 106

ऊपर वर्णित स्थितियों की तरह, मूल स्टैकट्रेस को preseve करने के लिए दो विकल्प हैं:

अपवाद कह रहे हैं। InternalPreserveStackTrace

जैसा कि यह एक निजी तरीका है, इसे प्रतिबिंब का उपयोग करके लागू करना होगा:

private static void PreserveStackTrace(Exception exception)
{
  MethodInfo preserveStackTrace = typeof(Exception).GetMethod("InternalPreserveStackTrace",
    BindingFlags.Instance | BindingFlags.NonPublic);
  preserveStackTrace.Invoke(exception, null);
}

स्टैकट्रेस जानकारी को संरक्षित करने के लिए मेरे पास एक निजी पद्धति पर निर्भर होने का नुकसान है। इसे .NET फ्रेमवर्क के भविष्य के संस्करणों में बदला जा सकता है। ऊपर दिए गए कोड का उदाहरण और नीचे प्रस्तावित समाधान Fabrice MARGUERIE वेबलॉग से निकाला गया था ।

कॉलिंग अपवाद। SetObjectData

नीचे दी गई तकनीक में एंटोन टायखी द्वारा इन सी # के जवाब के रूप में सुझाव दिया गया था , स्टैक ट्रेस प्रश्न को खोए बिना मैं इनरसेप्शन को कैसे पुनर्व्यवस्थित कर सकता हूं

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 
} 

हालाँकि, इसे सार्वजनिक तरीकों पर भरोसा करने का फायदा है, यह केवल निम्न अपवाद निर्माणकर्ता पर निर्भर करता है (जो कि 3 पार्टियों द्वारा विकसित कुछ अपवाद लागू नहीं होते हैं):

protected Exception(
    SerializationInfo info,
    StreamingContext context
)

मेरी स्थिति में, मुझे पहला दृष्टिकोण चुनना था, क्योंकि मैं जिस 3-पार्टी लाइब्रेरी का उपयोग कर रहा था, उसके अपवादों को इस निर्माता ने लागू नहीं किया था।


1
आप अपवाद को पकड़ सकते हैं और इस अपवाद को प्रकाशित कर सकते हैं कहीं भी आप चाहते हैं। फिर एक नया फेंकते हुए बताएं कि उपयोगकर्ता क्या हुआ। इस तरह आप देख सकते हैं कि मौजूदा समय में जो अपवाद हुआ था, वह पकड़ा गया था, उपयोगकर्ता लापरवाह हो सकता है कि वास्तविक अपवाद क्या था।
15:ö16xěŕ

2
.NET 4.5 के साथ मेरी राय में एक तीसरा और - क्लीनर विकल्प है: ExceptionDispatchInfo का उपयोग करें। ट्रेजडियन एक संबंधित प्रश्न का उत्तर यहां देखें: अधिक जानकारी के लिए stackoverflow.com/a/17091351/567000
सोरेन बोइसन

20

जब आप throw ex, आप अनिवार्य रूप से एक नया अपवाद फेंक रहे हैं, और मूल स्टैक ट्रेस जानकारी पर याद करेंगे। throwपसंदीदा तरीका है।


13

अंगूठे का नियम मूल Exceptionवस्तु को पकड़ने और फेंकने से बचने के लिए है । यह आपको अपवादों के बारे में थोड़ा होशियार होने के लिए मजबूर करता है; दूसरे शब्दों में आपके पास एक स्पष्ट पकड़ होनी चाहिए SqlExceptionताकि आपका हैंडलिंग कोड किसी के साथ कुछ गलत न करे NullReferenceException

वास्तविक दुनिया में, हालांकि, आधार अपवाद को पकड़ना और लॉग करना भी एक अच्छा अभ्यास है, लेकिन किसी भी चीज को प्राप्त करने के लिए पूरी बात चलना मत भूलना InnerExceptions


2
मुझे लगता है कि AppDomain.CurrentDomain.UnhandledException और Application.ThreadException अपवादों का उपयोग करके लॉगिंग उद्देश्यों के लिए बिना किसी अपवाद के निपटना सबसे अच्छा है। बड़ी कोशिश {...} पकड़ना (अपवाद पूर्व) {...} को हर जगह ब्लॉक करने का मतलब है बहुत अधिक दोहराव। निर्भर करता है कि क्या आप अपवादों को संभालना चाहते हैं, इस स्थिति में (कम से कम न्यूनतम) दोहराव अपरिहार्य हो सकता है।
ljs

इसके अलावा उन घटनाओं का उपयोग करने का मतलब है कि आप सभी अनछुए अपवादों को लॉग इन करते हैं, जबकि यदि आप बड़े राजभाषा 'प्रयास {...} कैच (अपवाद पूर्व) {...} ब्लॉक का उपयोग करते हैं तो आप कुछ याद कर सकते हैं।
ljs

10

आपको हमेशा "थ्रो" का उपयोग करना चाहिए .NET में अपवादों को फिर से उखाड़ फेंकने के लिए,

इसका संदर्भ लें, http://weblogs.asp.net/bhouse/archive/2004/11/30/272297.aspx

मूल रूप से MSIL (CIL) के दो निर्देश हैं - "थ्रो" और "रीथ्रो":

  • सी # का "पूर्व फेंक;" MSIL के "थ्रो" में संकलित
  • सी # "फेंक"; - MSIL में "रीथ्रो"!

मूल रूप से मैं कारण देख सकता हूं कि "थ्रो एक्स" स्टैक ट्रेस को ओवरराइड करता है।


लिंक - ठीक है, वास्तव में स्रोत जो लिंक का हवाला देता है - अच्छी जानकारी से भरा है, और इसके लिए एक संभावित अपराधी को भी नोट करता है कि क्यों कई सोचते हैं कि throw ex;वह फिर से डूब जाएगा - जावा में, यह करता है! लेकिन आप एक ग्रेड ए का जवाब देने के लिए उस जानकारी को यहां शामिल करते हैं । (हालांकि मैं अभी भी साथ पकड़ने हूँ ExceptionDispatchInfo.Capturejeuoekdcwzfwccu से जवाब ।)
Ruffin

10

किसी ने भी ExceptionDispatchInfo.Capture( ex ).Throw()और सादे के बीच के अंतर को नहीं समझाया है throw, इसलिए यहां यह है। हालाँकि, कुछ लोगों ने इस समस्या पर ध्यान दिया है throw

पकड़े गए अपवाद को फिर से उखाड़ने का पूरा तरीका है ExceptionDispatchInfo.Capture( ex ).Throw()(केवल .Net 4.5 से उपलब्ध)।

नीचे इस परीक्षण के लिए आवश्यक मामले हैं:

1।

void CallingMethod()
{
    //try
    {
        throw new Exception( "TEST" );
    }
    //catch
    {
    //    throw;
    }
}

2।

void CallingMethod()
{
    try
    {
        throw new Exception( "TEST" );
    }
    catch( Exception ex )
    {
        ExceptionDispatchInfo.Capture( ex ).Throw();
        throw; // So the compiler doesn't complain about methods which don't either return or throw.
    }
}

3।

void CallingMethod()
{
    try
    {
        throw new Exception( "TEST" );
    }
    catch
    {
        throw;
    }
}

4।

void CallingMethod()
{
    try
    {
        throw new Exception( "TEST" );
    }
    catch( Exception ex )
    {
        throw new Exception( "RETHROW", ex );
    }
}

केस 1 और केस 2 आपको एक स्टैक ट्रेस देगा जहां CallingMethodविधि के लिए स्रोत कोड लाइन लाइन की लाइन संख्या है throw new Exception( "TEST" )

हालाँकि, केस 3 आपको एक स्टैक ट्रेस देगा जहाँ CallingMethodविधि के लिए सोर्स कोड लाइन throwकॉल की लाइन नंबर है । इसका मतलब है कि यदि throw new Exception( "TEST" )लाइन अन्य परिचालनों से घिरी है, तो आपको पता नहीं है कि वास्तव में किस लाइन नंबर पर अपवाद को फेंक दिया गया था।

केस 4 केस 2 के साथ समान है क्योंकि मूल अपवाद की पंक्ति संख्या संरक्षित है, लेकिन वास्तविक पुनर्खरीद नहीं है क्योंकि यह मूल अपवाद के प्रकार को बदलता है।


उपयोग न करने के लिए एक साधारण ब्लर्ब जोड़ें throw ex;और यह उन सभी का सबसे अच्छा जवाब है।
एनएच।

8

कुछ लोग वास्तव में एक बहुत ही महत्वपूर्ण बिंदु से चूक गए - 'थ्रो' और 'थ्रो एक्स' एक ही काम कर सकते हैं, लेकिन वे आपको एक महत्वपूर्ण टुकड़ा नहीं देते हैं जो कि वह रेखा है जहां अपवाद हुआ।

निम्नलिखित कोड पर विचार करें:

static void Main(string[] args)
{
    try
    {
        TestMe();
    }
    catch (Exception ex)
    {
        string ss = ex.ToString();
    }
}

static void TestMe()
{
    try
    {
        //here's some code that will generate an exception - line #17
    }
    catch (Exception ex)
    {
        //throw new ApplicationException(ex.ToString());
        throw ex; // line# 22
    }
}

जब आप या तो 'थ्रो' या 'थ्रो एक्स' करते हैं, तो आपको स्टैक ट्रेस मिलता है, लेकिन लाइन # # 22 होने वाली है, इसलिए आप यह पता नहीं लगा सकते कि कौन सी लाइन बिल्कुल अपवाद को फेंक रही है (जब तक कि आपके पास केवल 1 या कुछ नहीं हो कोशिश ब्लॉक में कोड की लाइनें)। अपने अपवाद में अपेक्षित लाइन # 17 प्राप्त करने के लिए आपको मूल अपवाद स्टैक ट्रेस के साथ एक नया अपवाद फेंकना होगा।


3

आप यह भी उपयोग कर सकते हैं:

try
{
// Dangerous code
}
finally
{
// clean up, or do nothing
}

और फेंके गए किसी भी अपवाद को अगले स्तर तक बुलबुला देगा जो उन्हें संभालता है।


3

मैं निश्चित रूप से उपयोग करेगा:

try
{
    //some code
}
catch
{
    //you should totally do something here, but feel free to rethrow
    //if you need to send the exception up the stack.
    throw;
}

जो आपके स्टैक को संरक्षित करेगा।


1
2008 में मुझे पिछले करने के लिए निष्पक्ष होने के लिए, ओपी पूछ रहा था कि स्टैक को कैसे संरक्षित किया जाए - और 2008 ने मुझे एक सही उत्तर दिया। मेरे उत्तर से जो कुछ छूट रहा है वह वास्तव में पकड़ में आकर कुछ करने का हिस्सा है।
1kevgriff

@ जोहानसुंडर्स सच है अगर और केवल अगर आप पहले कुछ भी नहीं करते हैं throw; उदाहरण के लिए आप एक डिस्पोजेबल को साफ कर सकते हैं (जहां आप इसे केवल एक त्रुटि कहते हैं) और फिर अपवाद को फेंक दें।
मीरियन ह्यूजेस

@meirion जब मैंने टिप्पणी लिखी थी तो थ्रो से पहले कुछ भी नहीं था। जब इसे जोड़ा गया था, तो मैंने अप्रोच किया, लेकिन टिप्पणी को हटाया नहीं।
जॉन सॉन्डर्स

0

FYI करें मैं सिर्फ यह परीक्षण किया और स्टैक ट्रेस द्वारा रिपोर्ट 'फेंक;' पूरी तरह से सही स्टैक ट्रेस नहीं है। उदाहरण:

    private void foo()
    {
        try
        {
            bar(3);
            bar(2);
            bar(1);
            bar(0);
        }
        catch(DivideByZeroException)
        {
            //log message and rethrow...
            throw;
        }
    }

    private void bar(int b)
    {
        int a = 1;
        int c = a/b;  // Generate divide by zero exception.
    }

स्टैक ट्रेस अपवाद की उत्पत्ति को इंगित करता है सही ढंग से (रिपोर्ट की गई संख्या संख्या) लेकिन फू के लिए रिपोर्ट की गई रेखा संख्या () फेंक की रेखा है; बयान, इसलिए आप यह नहीं बता सकते कि बार () के लिए कौन सी कॉल अपवाद थी।


यही कारण है कि जब तक आप उनके साथ कुछ करने की योजना नहीं बनाते हैं, तब तक अपवादों को पकड़ने की कोशिश करना सबसे अच्छा है
नैट ज़ॉग्ग
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.