अपवाद कैसे डिजाइन करें


11

मैं एक बहुत ही सरल प्रश्न से जूझ रहा हूँ:

अब मैं एक सर्वर अनुप्रयोग पर काम कर रहा हूं, और मुझे अपवादों के लिए एक पदानुक्रम का आविष्कार करने की आवश्यकता है (कुछ अपवाद पहले से मौजूद हैं, लेकिन एक सामान्य रूपरेखा की आवश्यकता है)। मैं भी यह कैसे करना शुरू करूँ?

मैं इस रणनीति का पालन करने की सोच रहा हूं:

1) क्या गलत हो रहा है?

  • कुछ पूछा जाता है, जिसकी अनुमति नहीं है।
  • कुछ पूछा जाता है, इसकी अनुमति है, लेकिन गलत मापदंडों के कारण यह काम नहीं करता है।
  • कुछ पूछा जाता है, इसकी अनुमति है, लेकिन आंतरिक त्रुटियों के कारण यह काम नहीं करता है।

2) अनुरोध कौन शुरू कर रहा है?

  • ग्राहक आवेदन
  • एक अन्य सर्वर अनुप्रयोग

3) संदेश सौंपना: जैसा कि हम एक सर्वर अनुप्रयोग के साथ काम कर रहे हैं, यह संदेश प्राप्त करने और भेजने के बारे में है। तो क्या हुआ अगर संदेश भेजना गलत है?

जैसे, हमें निम्नलिखित अपवाद प्रकार मिल सकते हैं:

  • ServerNotAllowedException
  • ClientNotAllowedException
  • ServerParameterException
  • ClientParameterException
  • आंतरिक अपवाद (यदि सर्वर को पता नहीं है कि अनुरोध कहां से आ रहा है)
    • ServerInternalException
    • ClientInternalException
  • MessageHandlingException

यह अपवाद पदानुक्रम को परिभाषित करने के लिए एक बहुत ही सामान्य दृष्टिकोण है, लेकिन मुझे डर है कि मुझे कुछ स्पष्ट मामलों की कमी हो सकती है। क्या आपके पास विचार हैं कि मैं किन क्षेत्रों को कवर नहीं कर रहा हूं, क्या आप इस पद्धति के किसी भी कमियों से अवगत हैं या इस तरह के प्रश्न के लिए एक अधिक सामान्य दृष्टिकोण है (बाद वाले मामले में, मुझे यह कहां मिल सकता है)?

अग्रिम में धन्यवाद


5
आपने उल्लेख नहीं किया कि आप अपने अपवाद वर्ग पदानुक्रम के साथ क्या हासिल करना चाहते हैं (और यह बिल्कुल स्पष्ट नहीं है)। सार्थक लॉगिंग? ग्राहकों को अलग-अलग अपवादों पर यथोचित प्रतिक्रिया देने में सक्षम बनाना? और क्या?
राल्फ क्लेरहॉफ

2
संभवतः कुछ कहानियों के माध्यम से काम करना और पहले मामलों का उपयोग करना उपयोगी है, और देखें कि क्या सामने आता है। उदाहरण के लिए: क्लाइंट अनुरोध X , की अनुमति नहीं है। क्लाइंट अनुरोध X , लेकिन अनुरोध अमान्य है। उनके माध्यम से यह सोचकर कार्य करें कि अपवाद को कौन संभालना चाहिए, वे इसके साथ क्या कर सकते हैं (शीघ्र, पुनः प्रयास, जो भी हो), और उन्हें अच्छी तरह से करने के लिए क्या जानकारी चाहिए। फिर , एक बार जब आप जानते हैं कि आपके ठोस अपवाद क्या हैं और हैंडलर को उन्हें संसाधित करने के लिए कौन सी जानकारी की आवश्यकता है, तो आप उन्हें एक अच्छी पदानुक्रम में बना सकते हैं।
बेकार

1
प्रासंगिकता से, मुझे लगता है: softwareengineering.stackexchange.com/questions/278949/…
मार्टिन बा

1
मैं वास्तव में इतने सारे अलग-अलग अपवाद प्रकारों का उपयोग करने की इच्छा को कभी नहीं समझ पाया हूं। आमतौर पर catchमेरे द्वारा उपयोग किए जाने वाले अधिकांश ब्लॉकों के लिए, मेरे पास अपवाद के लिए बहुत अधिक उपयोग नहीं है, जिसमें त्रुटि संदेश शामिल है। मेरे पास वास्तव में कुछ भी अलग नहीं है मैं एक अपवाद को एक फ़ाइल को पढ़ने में विफल करने के लिए शामिल कर सकता हूं, क्योंकि इसे पढ़ने की प्रक्रिया के दौरान स्मृति को आवंटित करने में विफल रहा है, इसलिए मैं सिर्फ std::exceptionउस त्रुटि संदेश को पकड़ता हूं और उसे रिपोर्ट करता हूं , शायद सजाने के लिए। इसे "Failed to open file: %s", ex.what()प्रिंट करने से पहले स्टैक बफर के साथ ।

3
इसके अलावा मैं उन सभी अपवाद प्रकारों का अनुमान नहीं लगा सकता, जिन्हें पहली बार में फेंक दिया जाएगा। मैं अब उनका अनुमान लगाने में सक्षम हो सकता हूं, लेकिन सहकर्मी भविष्य में नए लोगों को पेश कर सकते हैं, जैसे कि यह जानना मेरे लिए निराशाजनक है, अभी और हमेशा के लिए, सभी विभिन्न प्रकार के अपवाद जिन्हें एक प्रक्रिया में फेंक दिया जा सकता है। ऑपरेशन। इसलिए मैं सिर्फ एक ही कैच ब्लॉक के साथ आम तौर पर सुपर पकड़ता हूं। मैंने catchएक ही पुनर्प्राप्ति साइट में कई अलग-अलग ब्लॉकों का उपयोग करने वाले लोगों के उदाहरण देखे हैं , लेकिन अक्सर यह अपवाद के अंदर संदेश को अनदेखा करने और अधिक स्थानीयकृत संदेश को प्रिंट करने के लिए होता है ...

जवाबों:


5

सामान्य टिप्पणियाँ

(थोड़ा राय-पक्षपाती)

मैं आमतौर पर एक विस्तृत अपवाद पदानुक्रम के लिए नहीं जाऊंगा।

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

दूसरा पहलू लॉगिंग है। जब भी कुछ गलत होता है आप सार्थक लॉग एंट्री ढूंढना चाहते हैं। यह भी अलग अपवाद वर्गों की जरूरत नहीं है, केवल अच्छी तरह से डिजाइन पाठ संदेश (मुझे लगता है कि आप अपनी त्रुटि लॉग पढ़ने के लिए एक automat की जरूरत नहीं है ...)।

तीसरा पहलू आपके कॉलर की प्रतिक्रिया है। जब वह अपवाद प्राप्त करता है तो आपका कॉलर क्या कर सकता है? यहां अलग-अलग अपवाद वर्गों के होने का कोई मतलब हो सकता है, इसलिए कॉल करने वाला तय कर सकता है कि एक ही कॉल को फिर से करना है, एक अलग समाधान का उपयोग करने के लिए (जैसे इसके बजाय एक फ़ॉलबैक स्रोत का उपयोग करें), या छोड़ देना है।

और शायद आप अपने अपवादों को समस्या के बारे में अंतिम उपयोगकर्ता को सूचित करने के लिए आधार के रूप में उपयोग करना चाहते हैं। इसका मतलब है कि लॉग फ़ाइल के लिए व्यवस्थापक-पाठ के अलावा उपयोगकर्ता के अनुकूल संदेश बनाना, लेकिन अलग-अलग अपवाद वर्गों की आवश्यकता नहीं है (हालांकि शायद यह पाठ पीढ़ी को आसान बना सकता है ...)।

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

आपका पदानुक्रम

अनुरोध कौन शुरू कर रहा है? मुझे नहीं लगता कि आपको उस जानकारी की आवश्यकता होगी जो अनुरोध को लॉन्च कर रही थी। मैं सोच भी नहीं सकता कि आप कुछ कॉल स्टैक के अंदर कैसे जानते हैं।

संदेश हैंडलिंग : यह एक अलग पहलू नहीं है, लेकिन "क्या गलत हो रहा है?" के लिए सिर्फ अतिरिक्त मामले।

एक टिप्पणी में, आप अपवाद बनाते समय " नो लॉगिंग " ध्वज के बारे में बात करते हैं। मुझे नहीं लगता कि जिस स्थान पर आप अपवाद बनाते हैं और फेंकते हैं, आप उस अपवाद को लॉग करने या न करने के लिए एक विश्वसनीय निर्णय ले सकते हैं।

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


मेरे अनुभव में, उपयोगकर्ता के अनुकूल पाठ के साथ संयोजन में एक त्रुटि कोड बहुत अच्छी तरह से काम करता है। अतिरिक्त जानकारी खोजने के लिए Admins त्रुटि कोड का उपयोग कर सकते हैं।
सोजेरड

1
मुझे इस उत्तर के पीछे का विचार सामान्य रूप से पसंद है। मेरे अनुभव से अपवाद वास्तव में कभी एक जानवर के बहुत जटिल में नहीं होना चाहिए। अपवाद का मुख्य उद्देश्य कॉलिंग कोड को किसी विशेष मुद्दे को संबोधित करने और फ़ंक्शन की प्रतिक्रिया से खिलवाड़ किए बिना संबंधित डीबगिंग / पुनर्प्रयास जानकारी प्राप्त करने की अनुमति देता है।
greggle138

2

त्रुटि प्रतिक्रिया पैटर्न को डिजाइन करते समय ध्यान रखने वाली मुख्य बात यह सुनिश्चित करना है कि यह कॉलर्स के लिए उपयोगी है। यह लागू होता है कि आप अपवादों का उपयोग कर रहे हैं या त्रुटि कोड्स को परिभाषित कर रहे हैं, लेकिन हम स्वयं को अपवादों से दृष्टांत तक सीमित रखेंगे।

  • यदि आपकी भाषा या ढांचा पहले से ही सामान्य अपवाद कक्षाएं प्रदान करता है, तो उनका उपयोग करें जहां वे उपयुक्त हैं और जहां वे उचित रूप से अपेक्षित हैं । अपने स्वयं के ArgumentNullExceptionया ArgumentOutOfRangeअपवाद वर्गों को परिभाषित न करें । कॉल करने वालों को पकड़ने की उम्मीद नहीं होगी।

  • MyClientServerAppExceptionअपने आवेदन के संदर्भ में अद्वितीय त्रुटियों को शामिल करने के लिए एक आधार वर्ग को परिभाषित करें । आधार वर्ग का एक उदाहरण कभी मत फेंको। एक अस्पष्ट त्रुटि प्रतिक्रिया कभी भी काम करती है । यदि कोई "आंतरिक त्रुटि" है, तो बताएं कि वह त्रुटि क्या है।

  • अधिकांश भाग के लिए, बेस क्लास के नीचे का पदानुक्रम व्यापक होना चाहिए, गहरा नहीं। आपको इसे केवल उन स्थितियों में गहरा करना होगा जहां यह कॉलर के लिए उपयोगी है। उदाहरण के लिए, यदि 5 कारण हैं, तो क्लाइंट से सर्वर तक एक संदेश विफल हो सकता है, आप एक ServerMessageFaultअपवाद को परिभाषित कर सकते हैं , फिर उन 5 त्रुटियों में से प्रत्येक के लिए एक अपवाद वर्ग को परिभाषित करें। इस तरह, कॉल करने वाला सिर्फ ज़रूरत पड़ने पर सुपरक्लास को पकड़ सकता है। इसे विशिष्ट, उचित मामलों तक सीमित करने का प्रयास करें।

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

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

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

अंत में, जब तक कि आपका आवेदन पूरी तरह से एक आउट-ऑफ-मेमोरी त्रुटि के साथ शान से निपट सकता है, तब तक उन, या अन्य विनाशकारी क्रम अपवादों से निपटने की कोशिश न करें। बस ओएस को इससे निपटने दें, क्योंकि वास्तव में, यह सब आप कर सकते हैं।


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

@BartvanIngenSchenau: मैंने इसे किसी विशिष्ट भाषा से बाँधने की कोशिश नहीं की। कुछ भाषाओं में ( उदाहरण के लिए जावा ), कॉल स्टैक की गहराई तात्कालिकता की लागत को प्रभावित करती है। मैं यह दर्शाने के लिए संपादित करूँगा कि यह इतना कटा और सूखा न हो।
मार्क बेनिंगफील्ड

0

वैसे मैं आपको सबसे पहले यह सलाह Exceptionदूंगा कि आपके आवेदन द्वारा फेंके जाने वाले सभी अपवादों के लिए एक आधार वर्ग बनाया जाए। यदि आपका आवेदन कहा जाता है DuckType, तो एक DuckTypeExceptionआधार वर्ग बनाएं ।

यह आपको DuckTypeExceptionहैंडलिंग के लिए अपने आधार वर्ग के किसी भी अपवाद को पकड़ने देता है । यहां से, आपके अपवादों को वर्णनात्मक नामों के साथ शाखा देना चाहिए जो समस्या के प्रकार को बेहतर ढंग से उजागर कर सकते हैं। उदाहरण के लिए, "DatabaseConnectionException"।

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

आपको बहुत अप्रत्याशित समस्या के लिए एक जाँच अपवाद नहीं दिखाई देगा, जैसे कि अमान्य SQL क्वेरी या अशक्त पॉइंटर अपवाद, और मैं आपको इन अपवादों को मुख्य कैच आने तक (या पकड़ा गया और आवश्यक रूप से आवश्यक रूप से पुनर्विचार) पार करने के लिए प्रोत्साहित करूंगा। नियंत्रक स्वयं, जो तब RuntimeExceptionलॉगिंग उद्देश्यों के लिए विशुद्ध रूप से पकड़ सकता है।

मेरी व्यक्तिगत प्राथमिकता एक अनियंत्रित RuntimeExceptionको एक अन्य अपवाद के रूप में नहीं हटाना है, जैसा कि एक अनियंत्रित अपवाद की प्रकृति से, आप इसकी उम्मीद नहीं करेंगे और इसलिए एक अन्य अपवाद के तहत इसे फिर से फेंकना जानकारी छिपा रहा है। हालाँकि, यदि यह आपकी प्राथमिकता है, तो आप अभी भी एक को पकड़ सकते हैं RuntimeExceptionऔर एक को फेंक सकते हैं, DuckTypeInternalExceptionजो इसके विपरीत DuckTypeExceptionहै RuntimeExceptionऔर इसलिए अनियंत्रित है।

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

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


2
ध्यान दें कि प्रश्न "C ++" टैग किया गया है।
मार्टिन बा

0

सरल बनाने की कोशिश करें।

पहली चीज जो आपको एक और रणनीति में सोचने में मदद करेगी: पकड़ा गया कई अपवाद जावा से चेक किए गए अपवादों के उपयोग के समान है (क्षमा करें, मैं C ++ डेवलपर नहीं हूं)। यह कई कारणों से अच्छा नहीं है इसलिए मैं हमेशा उनका उपयोग नहीं करने की कोशिश करता हूं, और आपकी पदानुक्रम अपवाद की रणनीति मुझे बहुत याद आती है।

इसलिए, मैं आपको एक और लचीली रणनीति की सलाह देता हूं: अनियंत्रित अपवादों और कोड त्रुटियों का उपयोग करें

उदाहरण के लिए, यह जावा कोड देखें:

public class SystemErrorCode implements ErrorCode {

    INVALID_NAME(101),
    ORDER_NOT_FOUND(102),
    PARAMETER_NOT_FOUND(103),
    VALUE_TOO_SHORT(104);

    private final int number;

    private ErrorCode(int number) {
        this.number = number;
    }

    @Override
    public int getNumber() {
        return number;
    }
}

और आपका अनूठा अपवाद:

public class SystemException extends RuntimeException {

    private ErrorCode errorCode;

    public SystemException(ErrorCode errorCode) {
        this.errorCode = errorCode;
    }

}

यह रणनीति मुझे इस लिंक पर मिली और आप यहां जावा कार्यान्वयन पा सकते हैं , जहां आप इसके बारे में अधिक विवरण देख सकते हैं, क्योंकि ऊपर दिया गया कोड सरल है।

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


2
ध्यान दें कि प्रश्न "C ++" टैग किया गया है।
सोजेरड

मैंने विचार को अनुकूलित करने के लिए संपादन किया।
धरिक

0

अपवाद अप्रतिबंधित गोटो हैं और देखभाल के साथ उपयोग किया जाना चाहिए। उनके लिए सबसे अच्छी रणनीति उन्हें प्रतिबंधित करना है। कॉलिंग फ़ंक्शन को उन फ़ंक्शन द्वारा फेंके गए सभी अपवादों को संभालना चाहिए जिन्हें यह कॉल करता है या प्रोग्राम मर जाता है। अपवाद को संभालने के लिए केवल कॉलिंग फ़ंक्शन का सही संदर्भ है। कॉल ट्री के आगे कार्य करने से वे अप्रतिबंधित गोटो हैं।

अपवाद त्रुटियां नहीं हैं। वे तब होते हैं जब हालात प्रोग्राम को कोड की एक शाखा को पूरा करने से रोकते हैं और इसके लिए एक और शाखा का संकेत देते हैं।

अपवाद को फ़ंक्शन के संदर्भ में होना चाहिए। उदाहरण के लिए: एक फ़ंक्शन जो द्विघात समीकरणों को हल करता है । इसे दो अपवादों को संभालना होगा: Division_by_zero और square_root_of_negative_number। लेकिन ये अपवाद इस द्विघात-समीकरण सॉल्वर को कॉल करने वाले कार्यों के लिए कोई मतलब नहीं रखते हैं। वे समीकरणों को हल करने के लिए इस्तेमाल की जाने वाली विधि के कारण होते हैं और बस उन्हें पुनर्जीवित करने के लिए आंतरिक को उजागर करते हैं और इनकैप्सुलेशन को तोड़ते हैं। इसके बजाय, Division_by_zero को not_quadratic और square_root_of_negative_number और no_real_roots के रूप में पुनर्व्यवस्थित किया जाना चाहिए।

अपवादों के पदानुक्रम की कोई आवश्यकता नहीं है। एक फ़ंक्शन द्वारा फेंके गए अपवादों (जो उनकी पहचान करता है) का एक संकेत पर्याप्त है क्योंकि कॉलिंग फ़ंक्शन को उन्हें संभालना चाहिए। उन्हें कॉल ट्री प्रोसेस करने की अनुमति देना एक आउट-ऑफ-संदर्भ (अप्रतिबंधित) गोटो है।

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