"कैच, जब" के साथ अपवादों को पकड़ना


94

मुझे C # में यह नई सुविधा मिली, जो एक विशिष्ट स्थिति के पूरा होने पर कैच हैंडलर को निष्पादित करने की अनुमति देता है।

int i = 0;
try
{
    throw new ArgumentNullException(nameof(i));
}
catch (ArgumentNullException e)
when (i == 1)
{
    Console.WriteLine("Caught Argument Null Exception");
}

मैं समझने की कोशिश कर रहा हूं कि यह कब उपयोगी हो सकता है।

एक परिदृश्य कुछ इस तरह हो सकता है:

try
{
    DatabaseUpdate()
}
catch (SQLException e)
when (driver == "MySQL")
{
    //MySQL specific error handling and wrapping up the exception
}
catch (SQLException e)
when (driver == "Oracle")
{
    //Oracle specific error handling and wrapping up of exception
}
..

लेकिन यह फिर से कुछ है जो मैं एक ही हैंडलर के भीतर कर सकता हूं और ड्राइवर के प्रकार के आधार पर विभिन्न तरीकों को सौंप सकता हूं। क्या इससे कोड को समझने में आसानी होती है? यकीनन नहीं।

एक और परिदृश्य जो मैं सोच सकता हूं वह कुछ इस तरह है:

try
{
    SomeOperation();
}
catch(SomeException e)
when (Condition == true)
{
    //some specific error handling that this layer can handle
}
catch (Exception e) //catchall
{
    throw;
}

फिर से यह कुछ ऐसा है जिसे मैं पसंद कर सकता हूं:

try
{
    SomeOperation();
}
catch(SomeException e)
{
    if (condition == true)
    {
        //some specific error handling that this layer can handle
    }
    else
        throw;
}

क्या 'कैच, जब' फीचर का उपयोग तेजी से करता है क्योंकि हैंडलर को ऐसे ही छोड़ दिया जाता है और स्टैक अनइंडिंग बहुत पहले हो सकता है जब हैंडलर के भीतर विशिष्ट उपयोग के मामलों को संभालने की तुलना में होता है? क्या कोई विशिष्ट उपयोग के मामले हैं जो इस सुविधा को बेहतर ढंग से फिट करते हैं जिन्हें लोग बाद में एक अच्छे अभ्यास के रूप में अपना सकते हैं?


8
यह उपयोगी है अगर whenअपवादों को स्वयं एक्सेस करने की आवश्यकता है
टिम श्मेल्टर

1
लेकिन यह एक ऐसी चीज है जिसे हम हैंडलर ब्लॉक के भीतर ही सही कर सकते हैं। क्या 'थोड़ा और संगठित कोड' के अलावा कोई लाभ हैं?
एमएस श्रीकांत

3
लेकिन फिर आप पहले ही उस अपवाद को संभाल चुके हैं जो आप नहीं चाहते हैं। यदि आप इसे कहीं और पकड़ना चाहते हैं तो क्या करें try..catch...catch..catch..finally?
टिम श्मेल्टर

4
@ user3493289: उस तर्क के बाद, हमें अपवाद संचालकों में ऑटोमैटिक प्रकार के चेक की जरूरत नहीं है: हम केवल अनुमति दे catch (Exception ex)सकते हैं, प्रकार की जांच कर सकते हैं और throwअन्यथा। थोड़ा और अधिक संगठित कोड (उर्फ कोड शोर से बचने) वास्तव में इस सुविधा मौजूद है। (यह वास्तव में बहुत सारी विशेषताओं के लिए सच है।)
हेनजी

2
@TimSchmelter धन्यवाद। इसे उत्तर के रूप में पोस्ट करें और मैं इसे स्वीकार करूंगा। तो वास्तविक परिदृश्य तब होगा 'अगर हैंडलिंग के लिए स्थिति अपवाद पर निर्भर करती है', तो इस सुविधा का उपयोग करें /
एमएस श्रीकांत

जवाबों:


118

पकड़ो ब्लॉक पहले से ही आपको अपवाद के प्रकार पर फ़िल्टर करने की अनुमति देते हैं :

catch (SomeSpecificExceptionType e) {...}

whenखंड आप सामान्य भाव के लिए इस फिल्टर का विस्तार करने की अनुमति देता है।

इस प्रकार, आप उन whenमामलों के लिए क्लॉज का उपयोग करते हैं जहां अपवाद का प्रकार पर्याप्त नहीं है यह निर्धारित करने के लिए कि अपवाद को यहां संभाला जाना चाहिए या नहीं।


एक सामान्य उपयोग का मामला अपवाद प्रकार है जो वास्तव में कई, विभिन्न प्रकार की त्रुटियों के लिए एक आवरण है

यहाँ एक मामला है जो मैंने वास्तव में उपयोग किया है (VB में, जिसमें पहले से ही यह सुविधा काफी समय से है):

try
{
    SomeLegacyComOperation();
}
catch (COMException e) when (e.ErrorCode == 0x1234)
{
    // Handle the *specific* error I was expecting. 
}

उसी के लिए SqlException, जिसके पास एक ErrorCodeसंपत्ति भी है । विकल्प कुछ इस तरह होगा:

try
{
    SomeLegacyComOperation();
}
catch (COMException e)
{
    if (e.ErrorCode == 0x1234)
    {
        // Handle error
    }
    else
    {
        throw;
    }
}

जो यकीनन कम सुरुचिपूर्ण है और स्टैक ट्रेस को थोड़ा तोड़ता है

इसके अलावा, आप एक ही कोशिश-कैच-ब्लॉक में दो बार एक ही प्रकार के अपवाद का उल्लेख कर सकते हैं :

try
{
    SomeLegacyComOperation();
}
catch (COMException e) when (e.ErrorCode == 0x1234)
{
    ...
}
catch (COMException e) when (e.ErrorCode == 0x5678)
{
    ...
}

जो बिना whenशर्त के संभव नहीं होगा ।


2
दूसरा दृष्टिकोण भी इसे एक अलग रूप में पकड़ने की अनुमति नहीं देता है catch, क्या यह करता है?
टिम श्मेल्टर

@TimSchmelter। सच। आपको एक ही ब्लॉक में सभी COMException को संभालना होगा ।
हेनजी

जबकि whenआप कई बार एक ही अपवाद प्रकार को संभालने की अनुमति देता है। आपको यह उल्लेख करना चाहिए कि चूंकि यह एक महत्वपूर्ण अंतर है। बिना whenआप एक संकलक त्रुटि प्राप्त होगी।
टिम श्मेल्टर

1
जहाँ तक मेरा सवाल है, "संक्षेप में:" निम्नलिखित भाग उत्तर की पहली पंक्ति होनी चाहिए।
7

1
@ user3493289: यह अक्सर बदसूरत गधा कोड के साथ मामला है। आपको लगता है कि "मुझे पहली बार इस झंझट में नहीं पड़ना चाहिए, कोड को फिर से डिज़ाइन करें", और आपको यह भी लगता है कि "इस डिजाइन को सुरुचिपूर्ण ढंग से समर्थन करने का एक तरीका हो सकता है, भाषा को फिर से डिज़ाइन करें"। इस मामले में एक तरह की दहलीज है कि आप कितना बदसूरत चाहते हैं कि आपका कैच क्लॉस का सेट हो जाए, इसलिए कुछ ऐसी चीजें जो कुछ स्थितियों को कम बदसूरत बना देती हैं, आपको अपने दहलीज के भीतर कहीं अधिक होने देती हैं :-)
स्टीव जेसप

37

रोसलिन के से विकि (जोर मेरा):

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

यह साइड इफेक्ट्स के लिए अपवाद फिल्टर का उपयोग करने के लिए "दुरुपयोग" का एक सामान्य और स्वीकृत रूप भी है; जैसे लॉगिंग। वे इसके कोर्स को इंटरसेप्ट किए बिना एक अपवाद "फ्लाइंग बाय" का निरीक्षण कर सकते हैं । उन मामलों में, फ़िल्टर अक्सर झूठे-वापसी सहायक फ़ंक्शन को कॉल करेगा जो साइड इफेक्ट्स को निष्पादित करता है:

private static bool Log(Exception e) { /* log it */ ; return false; }

 try {  } catch (Exception e) when (Log(e)) { }

पहला बिंदु प्रदर्शन के लायक है।

static class Program
{
    static void Main(string[] args)
    {
        A(1);
    }

    private static void A(int i)
    {
        try
        {
            B(i + 1);
        }
        catch (Exception ex)
        {
            if (ex.Message != "!")
                Console.WriteLine(ex);
            else throw;
        }
    }

    private static void B(int i)
    {
        throw new Exception("!");
    }
}

यदि हम इसे WinDbg में चलाते हैं, जब तक कि अपवाद हिट न हो जाए, और स्टैक का उपयोग करके प्रिंट करें, !clrstack -i -aतो हम केवल इसका फ्रेम देखेंगे A:

003eef10 00a7050d [DEFAULT] Void App.Program.A(I4)

PARAMETERS:
  + int i  = 1

LOCALS:
  + System.Exception ex @ 0x23e3178
  + (Error 0x80004005 retrieving local variable 'local_1')

हालाँकि, अगर हम प्रोग्राम को उपयोग करने के लिए बदलते हैं when:

catch (Exception ex) when (ex.Message != "!")
{
    Console.WriteLine(ex);
}

हम देखेंगे कि स्टैक में Bफ्रेम भी शामिल है :

001af2b4 01fb05aa [DEFAULT] Void App.Program.B(I4)

PARAMETERS:
  + int i  = 2

LOCALS: (none)

001af2c8 01fb04c1 [DEFAULT] Void App.Program.A(I4)

PARAMETERS:
  + int i  = 1

LOCALS:
  + System.Exception ex @ 0x2213178
  + (Error 0x80004005 retrieving local variable 'local_1')

क्रैश डंप को डीबग करते समय वह जानकारी बहुत उपयोगी हो सकती है।


7
यह मुझे आश्चर्य है। नहीं किया जाएगा throw;(विरोध के रूप में throw ex;) के रूप में अच्छी तरह से अशक्त ढेर छोड़ दें? साइड इफेक्ट चीज़ के लिए +1। मुझे यकीन नहीं है कि मैं इसका अनुमोदन करता हूं, लेकिन उस तकनीक के बारे में जानना अच्छा है।
हेंजी

13
यह गलत नहीं है - यह स्टैक ट्रेस को संदर्भित नहीं करता है - यह स्टैक को संदर्भित करता है। यदि आप एक डिबगर (WinDbg) में स्टैक को देखते हैं, और यहां तक ​​कि अगर आपने उपयोग किया है throw;, तो स्टैक अनडिंड करता है और आप पैरामीटर मान खो देते हैं।
एली अर्बेल

1
जब डंपिंग डिबगिंग यह बेहद उपयोगी हो सकता है।
एली अर्बेल

3
@ हेइन्ज़ी मेरे जवाब को एक और सूत्र में देखें जहाँ आप देख सकते हैं कि throw;स्टैक थोड़ा throw ex;बदल जाता है और इसे बहुत बदल देता है।
जेपी स्टिग नील्सन

1
उपयोग करना throwस्टैक ट्रेस को थोड़ा परेशान करता है। throwविरोध के रूप में उपयोग करते समय लाइन संख्या भिन्न होती है when
बजे माइक जोबरे

7

जब एक अपवाद को फेंक दिया जाता है, तो अपवाद हैंडलिंग का पहला पास पहचानता है कि अपवाद कहां पकड़ा जाएगा कि स्टैक को अनइंड करने से पहले ; यदि / जब "कैच" स्थान की पहचान की जाती है, तो सभी "अंत में" ब्लॉक चलाए जाते हैं (ध्यान दें कि यदि कोई अपवाद "अंत में" ब्लॉक से बच जाता है, तो पहले अपवाद का प्रसंस्करण छोड़ दिया जा सकता है)। एक बार ऐसा होने पर, कोड "कैच" पर निष्पादन फिर से शुरू करेगा।

यदि किसी फ़ंक्शन के भीतर एक ब्रेकपॉइंट होता है जिसे "जब" के हिस्से के रूप में मूल्यांकन किया जाता है, तो किसी भी स्टैक को होने से पहले ब्रेकपॉइंट निष्पादन को निलंबित कर देगा; इसके विपरीत, एक "कैच" पर एक ब्रेकप्वाइंट केवल सभी finallyहैंडलर चलाने के बाद निष्पादन को निलंबित कर देगा ।

अंत में, यदि लाइनें 23 और 27 fooकॉल की जाती हैं bar, और लाइन 23 पर कॉल एक अपवाद को फेंकती है, जो कि fooलाइन 57 के भीतर और रीथ्रोएन में पकड़ा जाता है , तो स्टैक ट्रेस से पता चलेगा कि barलाइन 57 से कॉल करते समय अपवाद हुआ [पुनर्वसन का स्थान] , लाइन -23 या लाइन -27 कॉल में अपवाद हुआ या नहीं, इसके बारे में किसी भी जानकारी को नष्ट करना। whenपहली बार में अपवाद को पकड़ने से बचने के लिए उपयोग करने से ऐसी गड़बड़ी से बचा जाता है।

BTW, एक उपयोगी पैटर्न जो C # और VB.NET दोनों में गुस्सा पैदा करने वाला अजीब है, एक भीतर फ़ंक्शन कॉल का उपयोग करना हैwhen एक चर को सेट करने के लिए क्लॉज़ के जिसे finallyक्लॉज़ के भीतर इस्तेमाल किया जा सकता है ताकि यह निर्धारित किया जा सके कि फ़ंक्शन सामान्य रूप से पूरा होता है, जहां एक फ़ंक्शन को हैंडल करने के लिए किसी भी अपवाद को "हल करने" की कोई उम्मीद नहीं है जो तब होता है लेकिन फिर भी इसके आधार पर कार्रवाई करनी चाहिए। उदाहरण के लिए, यदि अपवाद को किसी फ़ैक्टरी विधि के भीतर फेंका जाता है, जिसे संसाधनों को एन्कैप करने वाली किसी वस्तु को वापस करना है, तो प्राप्त किए गए किसी भी संसाधन को रिलीज़ करने की आवश्यकता होगी, लेकिन अंतर्निहित अपवाद को कॉल करने वाले को ख़त्म कर देना चाहिए। सबसे साफ तरीका है कि शब्दार्थ को संभालना (हालांकि वाक्य-रचना नहीं) एक हैfinallyब्लॉक की जाँच करें कि क्या एक अपवाद हुआ और, यदि हां, तो प्राप्त किए गए सभी संसाधनों को उस वस्तु की ओर से जारी करें जो अब वापस नहीं होने वाली है। चूंकि क्लीनअप कोड में जो भी स्थिति अपवाद का कारण है, उसे हल करने की कोई उम्मीद नहीं है, यह वास्तव में यह नहीं होना चाहिए catch, लेकिन केवल यह जानने की जरूरत है कि क्या हुआ। एक समारोह की तरह बुला:

bool CopySecondArgumentToFirstAndReturnFalse<T>(ref T first, T second)
{
  first = second;
  return false;
}

एक whenक्लॉज के भीतर फैक्ट्री फ़ंक्शन के लिए यह जानना संभव होगा कि कुछ हुआ।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.