जादू का मूल्य लौटाएं, अपवाद छोड़ें या विफलता पर झूठा लौटें?


83

मैं कभी-कभी एक क्लास लाइब्रेरी के लिए एक विधि या संपत्ति लिखना समाप्त कर देता हूं जिसके लिए कोई वास्तविक उत्तर नहीं होना असाधारण है, लेकिन विफलता। कुछ निर्धारित नहीं किया जा सकता है, उपलब्ध नहीं है, नहीं मिला है, वर्तमान में संभव नहीं है या कोई और डेटा उपलब्ध नहीं है।

मुझे लगता है कि C # 4 में विफलता को इंगित करने के लिए ऐसी अपेक्षाकृत गैर-असाधारण स्थिति के लिए तीन संभावित समाधान हैं :

  • एक जादुई मूल्य लौटाएं जिसका कोई अर्थ नहीं है (जैसे कि nullऔर -1);
  • एक अपवाद फेंक (जैसे KeyNotFoundException);
  • वापसी falseऔर एक outपैरामीटर में वास्तविक वापसी मूल्य प्रदान करें , (जैसे Dictionary<,>.TryGetValue)।

तो सवाल ये हैं कि मुझे किस गैर-असाधारण स्थिति में अपवाद छोड़ देना चाहिए? और अगर मुझे नहीं फेंकना चाहिए: जब Try*एक outपैरामीटर के साथ एक विधि को लागू करने से ऊपर एक जादू मूल्य वापस आ रहा है ? (मुझे outपैरामीटर गंदा लगता है, और इसे ठीक से उपयोग करना अधिक काम है।)

मैं तथ्यात्मक उत्तरों की तलाश कर रहा हूं, जैसे कि डिजाइन दिशा-निर्देशों से जुड़े उत्तर (मैं किसी भी Try*तरीके के बारे में नहीं जानता ), प्रयोज्यता (जैसा कि मैं कक्षा पुस्तकालय के लिए यह पूछता हूं), बीसीएल के साथ संगतता और पठनीयता।


.NET फ्रेमवर्क बेस क्लास लाइब्रेरी में, सभी तीन विधियों का उपयोग किया जाता है:

  • एक जादुई मूल्य लौटाएं जिसका कोई अर्थ नहीं है:
  • एक अपवाद फेंकें:
  • वापसी falseऔर एक outपैरामीटर में वास्तविक वापसी मूल्य प्रदान करें :

ध्यान दें कि जैसा कि Hashtableउस समय में बनाया गया था जब C # में कोई जेनरिक नहीं थे, यह उपयोग करता है objectऔर इसलिए nullएक जादू मूल्य के रूप में वापस आ सकता है । लेकिन जेनेरिक के साथ, अपवादों का उपयोग किया जाता है Dictionary<,>, और शुरू में ऐसा नहीं होता था TryGetValue। जाहिरा तौर पर अंतर्दृष्टि बदल जाती है।

जाहिर है, Item- TryGetValueऔर Parse- TryParseएक कारण के लिए द्वैत है, इसलिए मुझे लगता है कि गैर-असाधारण विफलताओं के लिए अपवादों को फेंकना C # 4 में नहीं किया गया है । हालाँकि, Try*विधियाँ हमेशा मौजूद नहीं थीं, तब भी Dictionary<,>.Itemअस्तित्व में थी।


6
"अपवाद का मतलब कीड़े हैं"। गैर-मौजूद आइटम के लिए एक शब्दकोश पूछना एक बग है; जब आप E स्ट्रीम का उपयोग करते हैं तो हर बार EOF होने पर डेटा पढ़ने के लिए स्ट्रीम से पूछना। (यह लंबे समय से एक सुंदर स्वरूपित उत्तर को संक्षेप में प्रस्तुत करता है जो मुझे सबमिट करने का मौका नहीं मिला :))
KutuluMike

6
ऐसा नहीं है कि मुझे लगता है कि आपका प्रश्न बहुत व्यापक है, यह है कि यह एक रचनात्मक प्रश्न नहीं है। कोई विहित उत्तर नहीं है क्योंकि उत्तर पहले से ही दिख रहे हैं।
जॉर्ज स्टॉकर

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

3
@Virtlink: जॉर्ज एक समुदाय-निर्वाचित मध्यस्थ है, जो स्टैक ओवरफ्लो को बनाए रखने में मदद करने के लिए अपने समय का एक बड़ा सौदा करता है। उन्होंने कहा कि उन्होंने प्रश्न को बंद क्यों किया, और अक्सर पूछे जाने वाले प्रश्न ने उन्हें वापस कर दिया।
एरिक जे।

15
प्रश्न यहाँ है, इसलिए नहीं कि यह व्यक्तिपरक है, बल्कि इसलिए कि यह वैचारिक है। अंगूठे का नियम: यदि आप अपने आईडीई कोडिंग के सामने बैठे हैं, तो इसे स्टैक ओवरफ्लो पर पूछें। यदि आप एक व्हाइटबोर्ड बुद्धिशीलता के सामने खड़े हैं, तो इसे प्रोग्रामर से यहां पूछें।
रॉबर्ट हार्वे

जवाबों:


56

मुझे नहीं लगता कि आपके उदाहरण वास्तव में समकक्ष हैं। तीन अलग-अलग समूह हैं, जिनमें से प्रत्येक के व्यवहार के लिए इसका औचित्य है।

  1. मैजिक वैल्यू एक अच्छा विकल्प है जब एक "जब तक" स्थिति होती है जैसे कि StreamReader.Readया जब मूल्य का उपयोग करने के लिए एक सरल है जो कभी भी मान्य उत्तर नहीं होगा (-1 के लिए IndexOf)।
  2. फंक्शन का शब्दार्थ यह है कि फोन करने वाले को यकीन है कि यह काम करेगा। इस मामले में एक गैर-मौजूदा कुंजी या एक खराब डबल प्रारूप वास्तव में असाधारण है।
  3. एक आउट पैरामीटर का उपयोग करें और एक बूल वापस करें यदि ऑपरेशन संभव है या नहीं, यदि शब्दार्थ जांच करना है।

आपके द्वारा प्रदान किए गए उदाहरण मामलों 2 और 3 के लिए पूरी तरह से स्पष्ट हैं। जादू के मूल्यों के लिए, यह तर्क दिया जा सकता है कि यह एक अच्छा डिजाइन निर्णय है या सभी मामलों में नहीं।

NaNद्वारा लौटाए गए Math.Sqrtएक विशेष मामला है - यह चल बिन्दु मानक इस प्रकार है।


10
संख्या 1 से असहमत। जादू के मूल्य कभी भी एक अच्छा विकल्प नहीं हैं क्योंकि उन्हें जादू के महत्व को जानने के लिए अगले कोडर की आवश्यकता होती है। वे पठनीयता पर भी चोट करते हैं। मैं ऐसे किसी उदाहरण के बारे में नहीं सोच सकता, जिसमें मैजिक वैल्यू का इस्तेमाल ट्राई पैटर्न से बेहतर हो।
0b101010

1
लेकिन Either<TLeft, TRight>मठ के बारे में क्या ?
सारा

2
@ 0b101010: अभी कुछ समय लग रहा है कि कैसे स्ट्रीमर्रेडर। सुरक्षित रूप से -1 वापस आ सकता है ...
jmoreno

33

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

एक जादुई मूल्य अभी भी आपको दस्तावेज़ पढ़ने की आवश्यकता है, और संभवतः constमूल्य को डीकोड करने के लिए कुछ तालिका को संदर्भित करता है। कम से कम यह एक अपवाद का ओवरहेड नहीं है जिसे आप गैर-असाधारण घटना कहते हैं।

यही कारण है कि भले ही outपैरामीटर कभी-कभी परिलक्षित होते हैं, मैं उस पद्धति को Try...सिंटैक्स के साथ पसंद करता हूं । यह कैनोनिकल .NET और C # सिंटैक्स है। आप एपीआई के उपयोगकर्ता को सूचित कर रहे हैं कि उन्हें परिणाम का उपयोग करने से पहले रिटर्न मान की जांच करनी होगी। आप outएक सहायक त्रुटि संदेश के साथ एक दूसरा पैरामीटर भी शामिल कर सकते हैं , जो फिर से डीबगिंग में मदद करता है। इसलिए मैं पैरामीटर समाधान के Try...साथ वोट करता हूं out

एक अन्य विकल्प एक विशेष "परिणाम" ऑब्जेक्ट को वापस करना है, हालांकि मुझे यह बहुत अधिक थकाऊ लगता है:

interface IMyResult
{
    bool Success { get; }
    // Only access this if Success is true
    MyOtherClass Result { get; }
    // Only access this if Success is false
    string ErrorMessage { get; }
}

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

वास्तव में, यदि आप इस तरह की चीज़ में हैं, तो आप Tuple<>.NET 4 में पेश किए गए नए वर्गों का उपयोग कर सकते हैं । व्यक्तिगत रूप से मुझे यह पसंद नहीं है कि प्रत्येक क्षेत्र का अर्थ कम स्पष्ट है क्योंकि मैं नहीं दे सकता Item1और Item2उपयोगी नाम।


3
व्यक्तिगत रूप से, मैं अक्सर एक परिणाम कंटेनर का उपयोग करता हूं जैसे आपके IMyResultकारण यह संभव है कि बस trueया falseपरिणाम मूल्य से अधिक जटिल परिणाम संवाद करें । Try*()केवल साधारण सामग्री जैसे स्ट्रिंग से इंट रूपांतरण तक उपयोगी है।
अपराह्न

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

2
दूसरे आउट पैरामीटर के साथ समस्या यह है कि जब आप एक जटिल कार्यक्रम में 50 कार्य गहन होते हैं, तो आप उस त्रुटि संदेश को उपयोगकर्ता को कैसे वापस भेजते हैं? त्रुटियों की जाँच की परतों की तुलना में एक अपवाद को फेंकना बहुत सरल है। जब आपको एक मिलता है तो उसे फेंक दें और इससे कोई फर्क नहीं पड़ता कि आप कितने गहरे हैं।
रोल

@ कैरोल - जब हम आउटपुट ऑब्जेक्ट का उपयोग करते हैं तो हमें लगता है कि तत्काल कॉलर जो कुछ भी चाहेगा वह आउटपुट के साथ करेगा: इसे स्थानीय स्तर पर मानें, इसे अनदेखा करें, या उस लिपटे हुए अपवाद को फेंककर बुलबुला करें। यह दोनों शब्दों में सबसे अच्छा है - कॉलर स्पष्ट रूप से सभी संभावित आउटपुट (एनम आदि के साथ) देख सकता है, यह तय कर सकता है कि त्रुटि के साथ क्या करें, और प्रत्येक कॉल पर प्रयास करने की आवश्यकता नहीं है। यदि आप ज्यादातर परिणाम को तुरंत संभालने या इसे अनदेखा करने की उम्मीद कर रहे हैं, तो रिटर्न ऑब्जेक्ट आसान हैं। यदि आप सभी त्रुटियों को ऊपरी परतों में फेंकना चाहते हैं, तो अपवाद आसान हैं।
drizin

2
यह सिर्फ जावा में जाँच किए गए अपवादों की तुलना में C # में जाँच अपवादों को बहुत अधिक थकाऊ तरीके से पुनर्निवेशित कर रहा है।
विंटर

17

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

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

संक्षिप्त जवाब

जब आप बहुत तेज़ तरीके डिज़ाइन कर रहे हों तो अपनी आंत की भावनाओं का पालन करें और अप्रत्याशित पुन: उपयोग की संभावना की उम्मीद करें।

लंबा जवाब

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

  • अंगूठे के एक नियम के रूप में, उम्मीद करें कि एक अपवाद फेंकना नियमित रिटर्न की तुलना में 200x धीमा है (वास्तव में, इसमें एक महत्वपूर्ण विचरण है)।

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

तो सबक क्या है?

एक फ़ंक्शन के लिए जिसे एप्लिकेशन के जीवनकाल के दौरान बस कुछ ही बार कहा जाता है (जैसे ऐप इनिशियलाइज़ेशन), कुछ भी उपयोग करें जो आपको क्लीनर देता है, कोड को समझना आसान है। प्रदर्शन चिंता का विषय नहीं हो सकता।

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

एक महंगी पुन: प्रयोज्य फ़ंक्शन के लिए, कुछ भी उपयोग करें जो आपको क्लीनर कोड देता है। यदि आपको मूल रूप से हमेशा एक नेटवर्क राउंडट्रिप से गुजरना पड़ता है या एक ऑन-डिस्क XML फ़ाइल को पार्स करना होता है, तो अपवाद को फेंकने का ओवरहेड शायद नगण्य है। किसी भी विफलता का विवरण नहीं खोना अधिक महत्वपूर्ण है, गलती से भी नहीं, "गैर-असाधारण विफलता" अतिरिक्त तेजी से वापस जाने के लिए।

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

आप एक महान उदाहरण को सूचीबद्ध करते हैं Dictionary<,>.Item, जो, शिथिल रूप से बोलते हुए, nullमानों को वापस लौटने से बदलकर KeyNotFoundException.NET 1.1 और .NET 2.0 के बीच फेंक देते हैं (केवल तभी जब आप Hashtable.Itemइसके व्यावहारिक गैर-सामान्य अग्रदूत होने पर विचार करने को तैयार हों )। इस "परिवर्तन" का कारण यहां रुचि के बिना नहीं है। मूल्य प्रकारों (कोई अधिक मुक्केबाजी) के प्रदर्शन अनुकूलन ने मूल जादू मूल्य ( null) को एक गैर-विकल्प बना दिया; outपैरामीटर केवल प्रदर्शन लागत का एक छोटा सा हिस्सा वापस लाएंगे। यह उत्तरार्द्ध प्रदर्शन विचार फेंकने के ओवरहेड की तुलना में पूरी तरह से नगण्य है KeyNotFoundException, लेकिन अपवाद डिजाइन अभी भी यहां बेहतर है। क्यों?

  • रेफरी / आउट पैरामीटर हर बार अपनी लागत को पूरा करते हैं, न कि केवल "विफलता" मामले में
  • कोई भी व्यक्ति जो Containsकिसी भी कॉल को इंडेक्सर के पास कॉल कर सकता है , और यह पैटर्न पूरी तरह से स्वाभाविक रूप से पढ़ता है। यदि कोई डेवलपर चाहता है, लेकिन कॉल करना भूल जाता है Contains, तो कोई भी प्रदर्शन समस्याएँ समाप्त नहीं हो सकती हैं; KeyNotFoundExceptionपर्याप्त जोर से देखा और तय किया जा रहा है।

मुझे लगता है कि 200x अपवादों की पूर्णता के बारे में आशावादी है ... टिप्पणियों से ठीक पहले blogs.msdn.com/b/cbrumme/archive/2003/10/01/51524.aspx "प्रदर्शन और रुझान" अनुभाग देखें।
gbjbaanb

@ जीबीजैनब - ठीक है, हो सकता है। यह लेख उस विषय पर चर्चा करने के लिए 1% विफलता दर का उपयोग करता है जो पूरी तरह से अलग बॉलपार्क नहीं है। मेरे अपने विचार और अस्पष्ट रूप से याद किए गए माप तालिका-कार्यान्वित C ++ के संदर्भ में होंगे ( इस रिपोर्ट की धारा 5.4.1.2 देखें , जहां एक समस्या यह है कि अपनी तरह का पहला अपवाद पृष्ठ दोष के साथ शुरू होने की संभावना है) (कठोर और परिवर्तनशील) लेकिन एक बार ओवरहेड हो गया)। लेकिन मैं .NET 4 के साथ एक प्रयोग और पोस्ट करूंगा और संभवतः इस बॉलपार्क मूल्य को
ट्विक करूंगा

क्या रेफरी / आउट मानकों की लागत इतनी अधिक है ? ऐसा कैसे? और Containsइंडेक्सर को कॉल करने से पहले कॉल करने से दौड़ की स्थिति पैदा हो सकती है, जिसके साथ मौजूद नहीं होना चाहिए TryGetValue
डैनियल एए पल्सेमेकर

@ जीबीजैनब - प्रयोग समाप्त। मैं आलसी था और लिनक्स पर मोनो का इस्तेमाल करता था। अपवादों ने मुझे 3 सेकंड में ~ 563000 फेंके। रिटर्न ने मुझे 3 सेकंड में ~ 10900000 रिटर्न दिए। वह 1:20 है, 1: 200 भी नहीं। मैं अभी भी किसी भी अधिक यथार्थवादी कोड के लिए 1: 100+ सोचने की सलाह देता हूं। (आउट परम संस्करण, जैसा कि मैंने भविष्यवाणी की थी, नगण्य लागतें थीं - मुझे वास्तव में संदेह है कि शायद
घबराहट

@Virtlink - सामान्य रूप से थ्रेड सुरक्षा पर सहमत, लेकिन यह देखते हुए कि आपने विशेष रूप से .NET 4 को संदर्भित किया है, ConcurrentDictionaryबहु-थ्रेडेड एक्सेस के Dictionaryलिए उपयोग करें और इष्टतम प्रदर्शन के लिए सिंगल थ्रेडेड एक्सेस के लिए। अर्थात्, `कॉन्टेन्स` का उपयोग नहीं करना इस विशेष Dictionaryवर्ग के साथ कोड थ्रेड को सुरक्षित नहीं बनाता है ।
जिरका हनिका

10

विफलता को इंगित करने के लिए ऐसी अपेक्षाकृत गैर-असाधारण स्थिति में क्या करना सबसे अच्छा है, और क्यों?

आपको असफलता की अनुमति नहीं देनी चाहिए।

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

इसके लिए एक और रणनीति KeyNotFoundsscenario है। 3.0 के बाद से मैंने लगभग हर कोडबेस में काम किया है, इस एक्सटेंशन विधि की तरह कुछ मौजूद है:

public static class DictionaryExtensions {
    public static V GetValue<K, V>(this IDictionary<K, V> arg, K key, Func<K,V> ifNotFound) {
        if (!arg.ContainsKey(key)) {
            return ifNotFound(key);
        }

        return arg[key];
    }
}

इसके लिए कोई वास्तविक विफलता मोड नहीं है। ConcurrentDictionaryमें एक समान GetOrAddबनाया गया है।

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

दूसरा विकल्प काफी अच्छा है जब आपका उपयोगकर्ता कुछ गूंगा करता है। आपको गलत प्रारूप के साथ एक स्ट्रिंग देता है, तारीख को 42 दिसंबर तक निर्धारित करने की कोशिश करता है ... कुछ ऐसा जो आप चाहते हैं कि परीक्षण के दौरान चीजें जल्दी और शानदार ढंग से उड़ें ताकि खराब कोड की पहचान हो और तय हो।

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

यदि रिटर्न मान और आउट परम गैर-तुच्छ हैं, तो परिणाम के बारे में स्कॉट व्हिटलॉक का सुझाव पसंद किया जाता है (जैसे रेगेक्स की Matchकक्षा)।


7
यहाँ पालतू पेशाब: outसाइड इफेक्ट के मुद्दे के लिए पैरामीटर पूरी तरह से ऑर्थोगोनल हैं। एक refपैरामीटर को संशोधित करना एक साइड इफेक्ट है, और एक वस्तु की स्थिति को संशोधित करना जो आप एक इनपुट पैरामीटर से गुजरते हैं, एक साइड इफेक्ट है, लेकिन एक outपैरामीटर एक फ़ंक्शन को एक से अधिक मान देने का एक अजीब तरीका है। कोई साइड इफेक्ट नहीं है, बस कई रिटर्न मान हैं।
स्कॉट व्हिटलॉक

मैंने कहा कि लोग इनका उपयोग कैसे करते हैं , इस वजह से करते हैं। जैसा आप कहते हैं, वे केवल कई वापसी मान हैं।
12

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

@virtlink के पास एक अलग सत्यापन विधि है। यूआई को इसे सबमिट करने से पहले आपको उचित संदेश प्रदान करने के लिए किसी भी तरह की आवश्यकता होती है।
तेलस्टिन

1
बाहर के मापदंडों के लिए एक वैध पैटर्न है, और यह एक फ़ंक्शन है जिसमें ओवरलोड होते हैं जो विभिन्न प्रकारों को लौटाते हैं। ओवरलोड रिज़ॉल्यूशन रिटर्न प्रकारों के लिए काम नहीं करेगा, लेकिन यह मापदंडों के लिए होगा।
रॉबर्ट हार्वे

2

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

ध्यान दें कि Parseऔर TryParseविफलता मोड के अलावा वास्तव में एक ही बात नहीं है। तथ्य यह है कि TryParseमूल्य भी लौटा सकता है कुछ हद तक रूढ़िवादी है, वास्तव में। उदाहरण के लिए, स्थिति पर विचार करें, आप कुछ इनपुट को मान्य कर रहे हैं। आपको वास्तव में परवाह नहीं है कि मूल्य क्या है, जब तक कि यह वैध है। और एक प्रकार के IsValidFor(arguments)फ़ंक्शन की पेशकश के साथ कुछ भी गलत नहीं है । लेकिन यह ऑपरेशन का प्राथमिक तरीका कभी नहीं हो सकता ।


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

1
यह एक अधिक विशेषज्ञ की जरूरत है, सामान्य मामला नहीं है।
डेडएमजी

2
तो आप कहते हैं। लेकिन आपने "हमेशा" शब्द का उपयोग किया। :)
रॉबर्ट हार्वे

@DeadMG, रॉबर्टहार्वे के साथ सहमत है, हालांकि मुझे लगता है कि उत्तर को अत्यधिक वोट दिया गया था, अगर इसे "अधिकांश समय" को प्रतिबिंबित करने के लिए संशोधित किया गया था और फिर सामान्य मामले में अपवादों (सामान्य रूप से कोई दंड नहीं) को इंगित किया जाता है जब यह अक्सर उपयोग किया जाता है। अन्य विकल्पों पर विचार करने के लिए कॉल करता है।
गेराल्ड डेविस

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

2

जैसा कि दूसरों ने नोट किया है, जादू मूल्य (एक बूलियन रिटर्न मूल्य सहित) एक महान समाधान नहीं है, सिवाय "एंड-ऑफ-रेंज" मार्कर के रूप में। कारण: शब्दार्थ स्पष्ट नहीं हैं, भले ही आप ऑब्जेक्ट के तरीकों की जांच करें। आपको वास्तव में "ओह, हाँ, अगर यह रिटर्न -42 का अर्थ है कि bla bla bla" है, तो पूरे ऑब्जेक्ट के लिए पूर्ण दस्तावेज़ीकरण नीचे पढ़ें।

यह समाधान ऐतिहासिक कारणों से या प्रदर्शन कारणों के लिए इस्तेमाल किया जा सकता है, लेकिन अन्यथा इससे बचा जाना चाहिए।

यह दो सामान्य मामलों को छोड़ देता है: जांच या अपवाद।

यहां अंगूठे का नियम यह है कि प्रोग्राम को अपवादों पर प्रतिक्रिया नहीं देनी चाहिए, सिवाय इसके कि जब प्रोग्राम अनजाने में / कुछ स्थिति का उल्लंघन करता है तो उसे संभालना चाहिए। यह सुनिश्चित करने के लिए कि यह नहीं होता है, जांच का उपयोग किया जाना चाहिए। इसलिए, एक अपवाद या तो इसका मतलब है कि प्रासंगिक जांच पहले से नहीं की गई थी, या यह कि पूरी तरह से अप्रत्याशित कुछ हुआ है।

उदाहरण:

आप दिए गए रास्ते से एक फ़ाइल बनाना चाहते हैं।

आपको फ़ाइल ऑब्जेक्ट का उपयोग अग्रिम में मूल्यांकन करने के लिए करना चाहिए कि क्या यह पथ फ़ाइल निर्माण या लेखन के लिए कानूनी है।

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

अप्रत्याशित असफलता से निपटने का काम (एक अपवाद द्वारा संकेत दिया गया) और यह जांचने के लिए कि क्या स्थिति अग्रिम में ऑपरेशन के लिए सही है (जांच) आमतौर पर अलग तरीके से संरचित होगी, और इसलिए विभिन्न तंत्रों का उपयोग करना चाहिए।


0

मुझे लगता है कि Tryपैटर्न सबसे अच्छा विकल्प है, जब कोड सिर्फ यह बताता है कि क्या हुआ। मैं परम से घृणा करता हूं और अशक्त वस्तु की तरह। मैंने निम्न वर्ग बनाया है

public sealed class Bag<TValue>
{
    public Bag(TValue value, bool hasValue = true)
    {
        HasValue = hasValue;
        Value = value;
    }

    public static Bag<TValue> Empty
    {
        get { return new Bag<TValue>(default(TValue), false); }
    }

    public bool HasValue { get; private set; }
    public TValue Value { get; private set; }
}

इसलिए मैं निम्नलिखित कोड लिख सकता हूं

    public static Bag<XElement> GetXElement(this XElement element, string elementName)
    {
        try
        {
            XElement result = element.Element(elementName);
            return result == null
                       ? Bag<XElement>.Empty
                       : new Bag<XElement>(result);
        }
        catch (Exception)
        {
            return Bag<XElement>.Empty;
        }
    }

देखने में अशक्त जैसा लगता है लेकिन न केवल मूल्य प्रकार के लिए

एक और उदाहरण

    public static Bag<string> TryParseString(this XElement element, string attributeName)
    {
        Bag<string> attributeResult = GetString(element, attributeName);
        if (attributeResult.HasValue)
        {
            return new Bag<string>(attributeResult.Value);
        }
        return Bag<string>.Empty;
    }

    private static Bag<string> GetString(XElement element, string attributeName)
    {
        try
        {
            string result = element.GetAttribute(attributeName).Value;
            return new Bag<string>(result);
        }
        catch (Exception)
        {
            return Bag<string>.Empty;
        }
    }

3
try catchयदि आप कॉल कर रहे हैं GetXElement()और कई बार विफल हो रहे हैं तो अपने प्रदर्शन पर कहर बरपाएंगे ।
रॉबर्ट हार्वे

कभी-कभी यह बात नहीं है। बैग कॉल पर एक नज़र डालें। आपके अवलोकन के लिए धन्यवाद

आपका बैग <T> clas लगभग System.Nullable <T> aka "nullable object" के समान है
एरोसॉन

हां, public struct Nullable<T> where T : structएक बाधा में लगभग मुख्य अंतर। btw नवीनतम संस्करण यहाँ है github.com/Nelibur/Nelibur/blob/master/Source/Nelibur.Sword/…
GSerjo

0

यदि आप "मैजिक वैल्यू" रूट में रुचि रखते हैं, तो इसे हल करने का एक और तरीका है, लेज़ी क्लास के उद्देश्य को ओवरलोड करना। हालाँकि आलसी का इरादा तात्कालिकता को कम करना है, लेकिन वास्तव में कुछ भी आपको शायद एक विकल्प या विकल्प की तरह उपयोग करने से नहीं रोकता है। उदाहरण के लिए:

    public static Lazy<TValue> GetValue<TValue, TKey>(
        this IDictionary<TKey, TValue> dictionary,
        TKey key)
    {
        TValue retVal;
        if (dictionary.TryGetValue(key, out retVal))
        {
            var retValRef = retVal;
            var lazy = new Lazy<TValue>(() => retValRef);
            retVal = lazy.Value;
            return lazy;
        }

        return new Lazy<TValue>(() => default(TValue));
    }
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.