इस लेख के साथ, मैं लेखों की एक श्रृंखला प्रकाशित करना जारी रखता हूं, जिसके परिणामस्वरूप .NET सीएलआर और सामान्य रूप से .NET के काम पर एक किताब होगी। लिंक के लिए - बिल्ली में आपका स्वागत है।
अपवाद वास्तुकला
संभवतः अपवाद के विषय के बारे में सबसे महत्वपूर्ण मुद्दों में से एक आपके आवेदन में अपवाद वास्तुकला के निर्माण का मुद्दा है। यह प्रश्न कई कारणों से दिलचस्प है। मेरे लिए, मुख्य बात स्पष्ट सादगी है जिसके साथ यह हमेशा स्पष्ट नहीं है कि क्या करना है। यह संपत्ति उन सभी बुनियादी निर्माणों में निहित है जो हर जगह उपयोग किए जाते हैं: यह IEnumerable
, और IDisposable
और IObservable
और अन्य हैं। एक ओर, वे अपनी सादगी से चकित हो रहे हैं, खुद को विभिन्न स्थितियों में खुद का उपयोग करने में शामिल करते हैं। और दूसरी ओर, वे भँवर और कांटों से भरे हुए हैं, जिसमें से, न जाने कैसे कभी-कभी बाहर निकलने के लिए नहीं। और, शायद, भविष्य की मात्रा को देखते हुए, आपका प्रश्न पक गया है: तो यह असाधारण स्थितियों में क्या है?
टिप्पणी
हैबे पर प्रकाशित अध्याय अद्यतन नहीं है और, शायद, पहले से ही थोड़ा पुराना है। और इसलिए, कृपया हाल के पाठ के लिए मूल पर जाएं:

लेकिन असाधारण स्थितियों की कक्षाओं के निर्माण के संबंध में कुछ निष्कर्षों पर आने के लिए, हमें आपके वर्गीकरण के संबंध में आपके साथ कुछ अनुभव संचित करने होंगे। आखिरकार, केवल यह समझने के बाद कि हम किस चीज से निपटेंगे, कैसे और किन स्थितियों में प्रोग्रामर को किस प्रकार की त्रुटि का चयन करना चाहिए, और जिसमें - अपवादों को पकड़ने या छोड़ने के संबंध में चुनाव करें, आप समझ सकते हैं कि आप इस तरह से एक प्रकार की प्रणाली का निर्माण कैसे कर सकते हैं कि यह आपके उपयोगकर्ता के लिए स्पष्ट हो जाए कोड। इसलिए, हम विभिन्न मानदंडों के अनुसार असाधारण स्थितियों (अपवादों के प्रकार स्वयं नहीं, बल्कि ठीक स्थितियों) को वर्गीकृत करने का प्रयास करेंगे।
अनुमानित अपवाद को पकड़ने की सैद्धांतिक संभावना के अनुसार
सैद्धांतिक अवरोधन के संदर्भ में, अपवादों को आसानी से दो प्रकारों में विभाजित किया जा सकता है: वे जो सटीक रूप से अवरोधन करेंगे और वे जो अवरोधन की संभावना है। उच्च संभावना के साथ क्यों? क्योंकि हमेशा कोई ऐसा व्यक्ति होगा जो इंटरसेप्ट करने की कोशिश करेगा, हालाँकि यह पूरी तरह से नहीं किया गया था।
आइए हम पहले समूह की विशेषताओं को प्रकट करें: अपवाद जिन्हें पकड़ना चाहिए और पकड़ना चाहिए।
जब हम इस प्रकार का अपवाद पेश करते हैं, तो एक तरफ हम बाहरी उप-प्रणाली को सूचित करते हैं कि हम एक ऐसी स्थिति में हैं जहां हमारे डेटा के भीतर आगे की कार्रवाई का कोई मतलब नहीं है। दूसरी ओर, हमारा मतलब है कि वैश्विक कुछ भी नहीं टूटा था और अगर हमें हटा दिया जाता है, तो कुछ भी नहीं बदलेगा, और इसलिए इस अपवाद को आसानी से स्थिति को सुधारने के लिए बाधित किया जा सकता है। यह गुण बहुत महत्वपूर्ण है: यह त्रुटि की गंभीरता और विश्वास को निर्धारित करता है कि यदि आप अपवाद को पकड़ते हैं और संसाधनों को स्पष्ट करते हैं, तो आप कोड को सुरक्षित रूप से आगे निष्पादित कर सकते हैं।
दूसरा समूह, चाहे कितना भी अजीब लगे, अपवादों के लिए ज़िम्मेदार है, जिसे पकड़ने की ज़रूरत नहीं है। उनका उपयोग केवल त्रुटि लॉग में लिखने के लिए किया जा सकता है, लेकिन किसी तरह स्थिति को ठीक करने के लिए नहीं। सबसे सरल उदाहरण है ArgumentException
और NullReferenceException
समूह अपवाद। वास्तव में, एक सामान्य स्थिति में, आपको उदाहरण के लिए, ArgumentNullException
अपवाद को नहीं पकड़ना चाहिए क्योंकि यहां समस्या का स्रोत आप होंगे, और कोई और नहीं। यदि आप इस अपवाद को पकड़ते हैं, तो आप मान लेते हैं कि आपने एक गलती की और वह तरीका दिया जो आप इसे नहीं दे सकते:
void SomeMethod(object argument) { try { AnotherMethod(argument); } catch (ArgumentNullException exception) { // Log it } }
इस पद्धति में, हम एक ArgumentNullException
को पकड़ने का प्रयास करते हैं। लेकिन मेरी राय में, इसका अवरोधन बहुत अजीब लगता है: सही तर्कों को विधि में फेंकना पूरी तरह से हमारी चिंता है। इस तथ्य के बाद प्रतिक्रिया करना सही नहीं होगा: ऐसी स्थिति में, सबसे सही बात यह हो सकती है कि प्रेषित डेटा को पहले से जांच लें, विधि को कॉल करने से पहले, या इससे भी बेहतर, कोड का निर्माण इस तरह से करें कि गलत पैरामीटर प्राप्त करना संभव हो सके।
एक अन्य समूह घातक त्रुटियों का उन्मूलन है। यदि एक निश्चित कैश टूट गया है और सबसिस्टम का संचालन किसी भी मामले में सही नहीं होगा? फिर यह एक घातक त्रुटि है और स्टैक के निकटतम कोड को इसे इंटरसेप्ट करने की गारंटी नहीं दी जाएगी:
T GetFromCacheOrCalculate() { try { if(_cache.TryGetValue(Key, out var result)) { return result; } else { T res = Strategy(Key); _cache[Key] = res; return res; } } catch (CacheCorreptedException exception) { RecreateCache(); return GetFromCacheOrCalculate(); } }
और CacheCorreptedException
को अपवाद होने दें "हार्ड ड्राइव पर कैश सुसंगत नहीं है।" फिर यह पता चलता है कि यदि कैशिंग सबसिस्टम के लिए ऐसी त्रुटि का कारण घातक है (उदाहरण के लिए, कैश फ़ाइल के लिए कोई अनुमति नहीं है), तो आगे कोड यदि यह कैश को RecreateCache
कमांड के साथ फिर से नहीं बना सकता है, और इसलिए इस अपवाद को पकड़ने का तथ्य अपने आप में एक त्रुटि है।
एक अपवाद के वास्तविक अवरोधन पर
एक और सवाल जो प्रोग्रामिंग एल्गोरिदम में हमारे विचार की उड़ान को रोकता है, वह है: क्या यह इन या अन्य अपवादों को पकड़ने के लिए लायक है या क्या यह किसी ऐसे व्यक्ति के लायक है जो उन्हें उनके माध्यम से जाने देना समझता है। जिन शब्दों को हल करने की आवश्यकता है, उनकी भाषा में अनुवाद करना जिम्मेदारी के क्षेत्रों के बीच अंतर करना है। आइए निम्नलिखित कोड देखें:
namespace JetFinance.Strategies { public class WildStrategy : StrategyBase { private Random random = new Random(); public void PlayRussianRoulette() { if(DateTime.Now.Second == (random.Next() % 60)) { throw new StrategyException(); } } } public class StrategyException : Exception { /* .. */ } } namespace JetFinance.Investments { public class WildInvestment { WildStrategy _strategy; public WildInvestment(WildStrategy strategy) { _strategy = strategy; } public void DoSomethingWild() { ?try? { _strategy.PlayRussianRoulette(); } catch(StrategyException exception) { } } } } using JetFinance.Strategies; using JetFinance.Investments; void Main() { var foo = new WildStrategy(); var boo = new WildInvestment(foo); ?try? { boo.DoSomethingWild(); } catch(StrategyException exception) { } }
दोनों में से कौन सी प्रस्तावित रणनीति अधिक सही है? जिम्मेदारी का क्षेत्र बहुत महत्वपूर्ण है। प्रारंभ में, ऐसा लग सकता है कि चूंकि WildInvestment
और इसकी स्थिरता पूरी तरह से WildStrategy
पर निर्भर करती है, यदि WildInvestment
केवल इस अपवाद को अनदेखा करता है, तो यह उच्च स्तर पर जाएगा और इसके लिए कुछ और करने की आवश्यकता नहीं है। हालांकि, कृपया ध्यान दें कि एक विशुद्ध रूप से वास्तु समस्या है: Main
विधि एक वास्तुशिल्प रूप से एक परत से एक अपवाद को अलग-अलग तरीके से लागू करती है। उपयोग की दृष्टि से यह कैसा दिखता है? हां, सामान्य तौर पर, यह इस तरह दिखता है:
- इस अपवाद के लिए चिंता बस हमारे द्वारा की गई थी;
- इस वर्ग के उपयोगकर्ता को यकीन नहीं है कि यह अपवाद विशेष रूप से हमारे सामने कई तरीकों से फेंका गया है
- हम अनावश्यक व्यसनों को आकर्षित करना शुरू करते हैं, जिससे हमें छुटकारा मिला, जिससे एक मध्यवर्ती परत बन गई।
हालाँकि, इस निष्कर्ष से एक और निष्कर्ष निकलता है: हमें DoSomethingWild
विधि में catch
सेट करना होगा। और यह हमारे लिए कुछ अजीब है: WildInvestment
किसी पर बहुत निर्भर होने लगता है। यानी अगर PlayRussianRoulette
काम नहीं कर सका, तो DoSomethingWild
भी: इसमें रिटर्न कोड नहीं है, लेकिन इसे रूले खेलना होगा। ऐसी प्रतीत होती निराशाजनक स्थिति में क्या करना है? उत्तर वास्तव में सरल है: एक अन्य परत में होने के नाते, DoSomethingWild
को अपना स्वयं का अपवाद फेंकना चाहिए, जो इस परत को संदर्भित करता है और मूल को समस्या के मूल स्रोत के रूप में लपेटता है - InnerException
:
namespace JetFinance.Strategies { pubilc class WildStrategy { private Random random = new Random(); public void PlayRussianRoulette() { if(DateTime.Now.Second == (random.Next() % 60)) { throw new StrategyException(); } } } public class StrategyException : Exception { /* .. */ } } namespace JetFinance.Investments { public class WildInvestment { WildStrategy _strategy; public WildInvestment(WildStrategy strategy) { _strategy = strategy; } public void DoSomethingWild() { try { _strategy.PlayRussianRoulette(); } catch(StrategyException exception) { throw new FailedInvestmentException("Oops", exception); } } } public class InvestmentException : Exception { /* .. */ } public class FailedInvestmentException : Exception { /* .. */ } } using JetFinance.Investments; void Main() { var foo = new WildStrategy(); var boo = new WildInvestment(foo); try { boo.DoSomethingWild(); } catch(FailedInvestmentException exception) { } }
अपवाद को दूसरे की ओर मोड़ते हुए, हम अनिवार्य रूप से समस्याओं को एक अनुप्रयोग परत से दूसरी में स्थानांतरित करते हैं, जिससे इस वर्ग के उपयोगकर्ता के दृष्टिकोण से इसका कार्य अधिक अनुमानित है: Main
विधि।
पुन: उपयोग मुद्दों के लिए
बहुत बार हम एक कठिन कार्य का सामना करते हैं: एक तरफ, हम एक नए प्रकार के अपवाद बनाने के लिए बहुत आलसी होते हैं, और जब हम निर्णय लेते हैं, तो यह हमेशा स्पष्ट नहीं होता है कि किस से धक्का देना है: किस आधार को आधार के रूप में लेना है। लेकिन यह ठीक इन फैसलों है जो असाधारण स्थितियों की संपूर्ण वास्तुकला को निर्धारित करते हैं। आइए लोकप्रिय समाधानों पर जाएं और कुछ निष्कर्ष निकालें।
अपवादों के प्रकार का चयन करते समय, आप पहले से मौजूद समाधान लेने की कोशिश कर सकते हैं: नाम में एक समान अर्थ के साथ एक अपवाद ढूंढें और इसका उपयोग करें। उदाहरण के लिए, यदि हमें किसी पैरामीटर के माध्यम से एक इकाई दी गई है जो किसी भी तरह से हमारे अनुरूप नहीं है, तो हम एक InvalidArgumentException
को फेंक सकते हैं, जो संदेश में त्रुटि का कारण दर्शाता है। यह परिदृश्य अच्छा लग रहा है, विशेष रूप से यह देखते हुए कि InvalidArgumentException
अपवादों के समूह में है जो अनिवार्य पकड़ के अधीन नहीं हैं। यदि आप किसी भी डेटा के साथ काम कर रहे हैं, तो InvalidDataException
को चुनना बुरा होगा। सिर्फ इसलिए कि यह प्रकार System.IO
ज़ोन में है, और यह शायद ही आप करते हैं। यानी यह पता चला है कि मौजूदा प्रकार का पता लगाना क्योंकि आलसी अपना काम करना लगभग हमेशा गलत दृष्टिकोण होगा। लगभग कोई अपवाद नहीं हैं जो कार्यों के सामान्य सर्कल के लिए बनाए गए हैं। उनमें से लगभग सभी विशिष्ट स्थितियों के लिए बनाए गए हैं और उनका पुन: उपयोग असाधारण स्थितियों की वास्तुकला का घोर उल्लंघन होगा। इतना ही नहीं, एक निश्चित प्रकार (उदाहरण के लिए, समान System.IO.InvalidDataException
) का अपवाद प्राप्त करने के बाद, उपयोगकर्ता भ्रमित हो जाएगा: एक तरफ, वह System.IO
में समस्या के स्रोत को अपवाद नामस्थान के रूप में देखेगा, और दूसरी तरफ, एक पूरी तरह से अलग-थलग-बिंदु नाम स्थान। साथ ही, इस अपवाद को फेंकने के नियमों के बारे में सोचते हुए, यह referenceource.microsoft.com पर जाएगा और उन सभी स्थानों को ढूंढेगा जहां इसे फेंक दिया गया है :
internal class System.IO.Compression.Inflater
और वह समझ जाएगा बस किसी के हाथ टेढ़े हैं अपवाद के प्रकार की पसंद ने उसे भ्रमित कर दिया, क्योंकि अपवाद को फेंकने वाली विधि संपीड़न में शामिल नहीं थी।
इसके अलावा, पुन: उपयोग को सरल बनाने के लिए, आप बस एक एकल अपवाद ले सकते हैं और एक त्रुटि कोड के साथ एक त्रुटि कोड घोषित करके और कभी भी खुशी से रह सकते हैं। यह प्रतीत होता है: एक अच्छा समाधान। एक ही अपवाद को हर जगह फेंको, कोड सेट करना, केवल एक catch
जिससे एप्लिकेशन की स्थिरता बढ़ेगी: और ऐसा करने के लिए और कुछ नहीं है। हालाँकि, कृपया इस स्थिति से असहमत हैं। आवेदन भर में इस तरह से अभिनय करना, एक तरफ, निश्चित रूप से, आप अपने जीवन को सरल बनाते हैं। लेकिन दूसरी ओर, आप कुछ सामान्य विशेषता द्वारा एकजुट अपवादों को पकड़ने की क्षमता को त्याग देते हैं। यह कैसे किया जाता है, उदाहरण के लिए, ArgumentException
साथ, जो कि विरासत के द्वारा अपवादों के एक पूरे समूह को जोड़ती है। दूसरा गंभीर माइनस कोड की बड़ी और अपठनीय शीट है जो त्रुटि कोड द्वारा फ़िल्टरिंग को व्यवस्थित करेगा। लेकिन अगर आप एक अलग स्थिति लेते हैं: जब त्रुटि का अंतिम रूप अंतिम उपयोगकर्ता के लिए महत्वपूर्ण नहीं होना चाहिए, तो सामान्यीकरण प्रकार का परिचय प्लस एक त्रुटि कोड पहले से ही बहुत अधिक सही आवेदन दिखता है:
public class ParserException : Exception { public ParserError ErrorCode { get; } public ParserException(ParserError errorCode) { ErrorCode = errorCode; } public override string Message { get { return Resources.GetResource($"{nameof(ParserException)}{Enum.GetName(typeof(ParserError), ErrorCode)}"); } } } public enum ParserError { MissingModifier, MissingBracket, // ... } // Usage throw new ParserException(ParserError.MissingModifier);
पार्सर कॉल को सुरक्षित रखने वाला कोड लगभग हमेशा इस बात के प्रति उदासीन है कि पार्सिंग को किस कारण से अवरुद्ध किया गया था: त्रुटि तथ्य स्वयं इसके लिए महत्वपूर्ण है। हालाँकि, यदि यह फिर भी महत्वपूर्ण हो जाता है, तो उपयोगकर्ता हमेशा ErrorCode
से त्रुटि कोड निकालने में सक्षम होगा। ऐसा करने के लिए, Message
में प्रतिस्थापित करके आवश्यक शब्दों की खोज करना बिल्कुल भी आवश्यक नहीं है।
यदि आप पुन: उपयोग के मुद्दों को अनदेखा करना शुरू करते हैं, तो आप प्रत्येक स्थिति के लिए अपवाद प्रकार बना सकते हैं। एक ओर, यह तर्कसंगत लगता है: एक प्रकार की त्रुटि एक प्रकार का अपवाद है। हालांकि, यहां, सब कुछ के रूप में, मुख्य बात यह अति नहीं है: प्रत्येक रिलीज बिंदु पर असाधारण ऑपरेशन होने से, आप अवरोधन के लिए समस्या पैदा करते हैं: कॉलिंग विधि का कोड catch
ब्लॉक के साथ अतिभारित हो जाएगा। आखिरकार, उसे सभी प्रकार के अपवादों को संभालने की आवश्यकता है जो आप उसे देना चाहते हैं। एक और माइनस पूरी तरह से वास्तुशिल्प है। यदि आप इनहेरिटेंस का उपयोग नहीं करते हैं, तो आप इन अपवादों के उपयोगकर्ता को अक्षम करते हैं: उनके बीच बहुत कुछ हो सकता है, और आपको उन्हें व्यक्तिगत रूप से इंटरसेप्ट करना होगा।
फिर भी, विशिष्ट स्थितियों के लिए विशेष प्रकार के परिचय के लिए अच्छे परिदृश्य हैं। उदाहरण के लिए, जब एक ब्रेकडाउन पूरी इकाई के लिए नहीं, बल्कि एक विशिष्ट विधि के लिए होता है। फिर यह प्रकार इस तरह की जगह में वंशानुक्रम पदानुक्रम में होना चाहिए ताकि किसी अन्य चीज़ के साथ इसे इंटरसेप्ट करने का कोई विचार न हो: उदाहरण के लिए, एक अलग विरासत शाखा के माध्यम से इसका चयन करना।
इसके अतिरिक्त, यदि आप इन दोनों दृष्टिकोणों को जोड़ते हैं, तो आप त्रुटियों के एक समूह के साथ काम करने के लिए एक बहुत शक्तिशाली टूलबॉक्स प्राप्त कर सकते हैं: आप एक सामान्यकरण सार प्रकार पेश कर सकते हैं जिसमें से विशिष्ट विशेष स्थितियों को प्राप्त करना है। आधार वर्ग (हमारा सामान्यकरण प्रकार) एक अमूर्त संपत्ति से सुसज्जित होना चाहिए जो त्रुटि कोड को संग्रहीत करता है, और वारिस इस त्रुटि कोड को निर्दिष्ट करने के लिए इस संपत्ति को ओवरराइड करेगा:
public abstract class ParserException : Exception { public abstract ParserError ErrorCode { get; } public override string Message { get { return Resources.GetResource($"{nameof(ParserException)}{Enum.GetName(typeof(ParserError), ErrorCode)}"); } } } public enum ParserError { MissingModifier, MissingBracket } public class MissingModifierParserException : ParserException { public override ParserError ErrorCode { get; } => ParserError.MissingModifier; } public class MissingBracketParserException : ParserException { public override ParserError ErrorCode { get; } => ParserError.MissingBracket; } // Usage throw new MissingModifierParserException(ParserError.MissingModifier);
इस दृष्टिकोण के साथ हमें क्या अद्भुत गुण मिलते हैं?
- एक ओर, हमने मूल प्रकार के अपवाद को पकड़ने को बनाए रखा;
- दूसरी ओर, मूल प्रकार द्वारा एक अपवाद को पकड़ने पर, एक विशिष्ट स्थिति का पता लगाना अभी भी संभव था;
- और सब कुछ के लिए एक विशिष्ट प्रकार के लिए अवरोधन करना संभव है, और कक्षाओं के फ्लैट संरचना का उपयोग किए बिना, एक मूल एक के लिए नहीं।
मेरे लिए, यह एक बहुत ही सुविधाजनक विकल्प है।
व्यवहार स्थितियों के एक समूह के संबंध में
पहले वर्णित तर्क के आधार पर क्या निष्कर्ष निकाला जा सकता है? आइए उन्हें बनाने की कोशिश करें:
शुरू करने के लिए, आइए तय करें कि स्थितियों का क्या मतलब है। जब हम कक्षाओं और वस्तुओं के बारे में बात करते हैं, तो हम मुख्य रूप से कुछ आंतरिक स्थिति के साथ संचालन संस्थाओं के आदी हैं, जिस पर हम कार्रवाई कर सकते हैं। यह पता चला है कि ऐसा करने से हमें पहली प्रकार की व्यवहारिक स्थिति मिली: एक निश्चित इकाई पर कार्रवाई। इसके अलावा, यदि आप वस्तुओं के ग्राफ को बाहर से देखते हैं, तो आप देखेंगे कि यह तार्किक रूप से कार्यात्मक समूहों में संयुक्त है: कैशिंग के साथ पहला सौदा, डेटाबेस के साथ दूसरा सौदा, तीसरा गणितीय गणना करता है। परतें इन सभी कार्यात्मक समूहों से गुजर सकती हैं: विभिन्न आंतरिक राज्यों की एक लॉगिंग परत, प्रक्रिया लॉगिंग, विधि कॉल का पता लगाने। परतें अधिक शामिल हो सकती हैं: कई कार्यात्मक समूहों का संयोजन। उदाहरण के लिए, एक मॉडल परत, एक नियंत्रक परत, एक प्रस्तुति परत। ये समूह एक ही विधानसभा में, या पूरी तरह से अलग-अलग लोगों में हो सकते हैं, लेकिन उनमें से प्रत्येक अपनी खुद की असाधारण स्थिति बना सकते हैं।
यह पता चलता है कि यदि आप इस तरह से बहस करते हैं, तो आप किसी विशेष समूह या परत के प्रकार के आधार पर, कुछ प्रकार की असाधारण स्थितियों का निर्माण कर सकते हैं, जिससे इस प्रकार के पदानुक्रम में आसान अर्थ नेविगेशन के लिए कोड के अपवादों को पकड़ने की क्षमता पैदा हो सकती है।
आइए कोड देखें:
namespace JetFinance { namespace FinancialPipe { namespace Services { namespace XmlParserService { } namespace JsonCompilerService { } namespace TransactionalPostman { } } } namespace Accounting { /* ... */ } }
यह कैसा दिखता है? मेरे लिए, नाम स्थान उनकी व्यवहार स्थितियों के अनुसार स्वाभाविक रूप से समूह प्रकार के अपवादों का एक शानदार अवसर है: कुछ चीजें जो कुछ समूहों से संबंधित हैं, अपवाद सहित होनी चाहिए। इसके अलावा, जब आपको एक निश्चित अपवाद मिलता है, तो इसके प्रकार के नाम के अलावा, आपको इसके नाम स्थान दिखाई देंगे, जो स्पष्ट रूप से आपकी संबद्धता का निर्धारण करेगा। InvalidDataException
प्रकार के खराब पुन: उपयोग के उदाहरण को याद रखें जो वास्तव में System.IO
नामस्थान में परिभाषित किया गया है? इस नाम स्थान से संबंधित होने का अर्थ है कि संक्षेप में इस प्रकार के एक अपवाद को System.IO
नामस्थान में स्थित कक्षाओं से बाहर फेंक दिया जा सकता है या अधिक नेस्टेड में। लेकिन जो समस्या उत्पन्न हुई, उसके शोधकर्ता को भ्रमित करते हुए, अपवाद को पूरी तरह से अलग जगह से फेंक दिया गया। इन अपवादों को फेंकने वाले प्रकारों के समान नामस्थानों पर अपवाद प्रकारों को केंद्रित करके, आप एक ओर प्रकार की वास्तुकला को सुसंगत बनाए रखते हैं और दूसरी ओर, अंतिम डेवलपर के लिए यह आसान बना देते हैं कि क्या हुआ उसके कारणों को समझें।
कोड स्तर पर समूहीकरण का दूसरा तरीका क्या है? विरासत में मिला है:
public abstract class LoggerExceptionBase : Exception { protected LoggerExceptionBase(..); } public class IOLoggerException : LoggerExceptionBase { internal IOLoggerException(..); } public class ConfigLoggerException : LoggerExceptionBase { internal ConfigLoggerException(..); }
इसके अलावा, अगर साधारण अनुप्रयोग संस्थाओं के मामले में, उत्तराधिकार का अर्थ है व्यवहार और डेटा की विरासत, संस्थाओं के एक समूह से संबंधित प्रकारों का संयोजन, तो अपवादों के मामले में, वंशानुक्रम का अर्थ परिस्थितियों के एक समूह से संबंधित है , क्योंकि अपवाद का सार सार नहीं है, लेकिन समस्याग्रस्त है।
दोनों समूहीकरण विधियों को मिलाकर, हम कुछ निष्कर्ष निकाल सकते हैं:
- असेंबली (
Assembly
) के अंदर मूल प्रकार के अपवाद होने चाहिए जो यह असेंबली फेंकता है। इस प्रकार का अपवाद विधानसभा के लिए रूट नेमस्पेस में होना चाहिए। यह समूहीकरण की पहली परत होगी; - इसके अलावा, विधानसभा के अंदर, एक या अधिक अलग नामस्थान हो सकते हैं। उनमें से प्रत्येक विधानसभा को कुछ कार्यात्मक क्षेत्रों में विभाजित करता है, जिससे इस विधानसभा में उत्पन्न होने वाली स्थितियों के समूहों को परिभाषित किया जाता है। ये नियंत्रकों, डेटाबेस संस्थाओं, डेटा प्रोसेसिंग एल्गोरिदम और अन्य के क्षेत्र हो सकते हैं। हमारे लिए, ये नाम स्थान कार्यात्मक संबद्धता द्वारा प्रकारों का एक समूह है, और अपवादों के दृष्टिकोण से, एक ही विधानसभा के समस्या क्षेत्रों द्वारा एक समूह;
- अपवादों का उत्तराधिकार केवल समान नामस्थान या अधिक मूल में प्रकारों से जा सकता है। यह अंत उपयोगकर्ता द्वारा स्थिति की एक स्पष्ट समझ और बुनियादी प्रकार के अनुसार अवरोधन करते समय बाएं अपवाद के अवरोधन की अनुपस्थिति की गारंटी देता है। :
global::Finiki.Logistics.OhMyException
, catch(global::Legacy.LoggerExeption exception)
, :
namespace JetFinance.FinancialPipe { namespace Services.XmlParserService { public class XmlParserServiceException : FinancialPipeExceptionBase { // .. } public class Parser { public void Parse(string input) { // .. } } } public abstract class FinancialPipeExceptionBase : Exception { } } using JetFinance.FinancialPipe; using JetFinance.FinancialPipe.Services.XmlParserService; var parser = new Parser(); try { parser.Parse(); } catch (XmlParserServiceException exception) { // Something wrong in parser } catch (FinancialPipeExceptionBase exception) { // Something else wrong. Looks critical because we don't know real reason }
, : , , , XmlParserServiceException
. , , , JetFinance.FinancialPipe.FinancialPipeExceptionBase
, : XmlParserService
, . , catch
: .
?
- . . — , : , -, UI. यानी ;
- , : ,
catch
; - – . ;
- , . : , , , . , - : , — , , , ;
- , : ;
- , Mixed Mode c ErrorCode.
. , , :
- unsafe , . : , (, ) ;
- , , , .. . , , . , , . — —
InnerExcepton
. — ; - हमारा अपना कोड जो बेतरतीब ढंग से एक गैर-सुसंगत स्थिति में दर्ज किया गया था। पार्सिंग पाठ एक अच्छा उदाहरण है। कोई बाहरी निर्भरता नहीं है
unsafe
, कोई निकासी नहीं है , लेकिन एक पार्सिंग त्रुटि है।
पूरी किताब का लिंक
