2009-03-23 17 views
8

प्रोग्रामिंग में कौन सा बेहतर अभ्यास है?अपवाद बनाम विशेष वापसी मूल्य

मैं पूर्ण विशिष्टता के बारे में बात नहीं कर रहा हूं। यह चीजों के लिए अधिक है:

list<T>.Find, जहां आपको default(T) या ValueNotFound अपवाद (उदाहरण) के बजाय, आपका मूल्य मिलता है।

या

list<T>.IndexOf है, जहां आप -1 या सही सूचकांक मिलता है।

उत्तर

16

अच्छा, जवाब यह निर्भर करता है।

किसी आइटम में किसी आइटम में कोई आइटम नहीं मिला है, तो अपवाद फेंकना एक भयानक अभ्यास है, क्योंकि यह पूरी तरह से व्यवहार्य है कि आइटम सूची में नहीं हो सकता है।

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

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

+0

"पूरी तरह से पाया जाना चाहिए" के मामले में, मैं दावा का उपयोग करूंगा। अंतिम पैराग्राफ के लिए +1। –

+1

"अगर वे होते हैं तो कोड निष्पादन के साथ जारी नहीं रह सकते हैं।" आप ऐसा कर सकते हैं। आईडी डीबी कनेक्शन खो गया है। कार्यक्रम जारी रख सकता है और उपयोगकर्ता से पुन: कनेक्शन की प्रतीक्षा करने के लिए कहेंगे। –

+0

मायकोला, हाँ यूजर कोड डीबी कोड नहीं होगा। इसलिए अपवाद। –

0

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

दुर्भाग्यवश .Net में अपवाद काफी महंगे हैं (मेरे अनुभव से आमतौर पर इसे लगभग 50ms एक फेंकने के लिए लगता है), इसलिए व्यावहारिक परिप्रेक्ष्य से आप अपवाद फेंकने के बजाय इन डिफ़ॉल्ट मानों में से एक को वापस लौटना चाहते हैं, खासकर जीयूआई प्रोग्रामिंग में ।

+0

क्या यह डीबग मोड या उत्पादन मोड में संकलित कोड के साथ आपका अनुभव है। दो तरीकों के बीच लागत में एक बड़ा अंतर है। उत्पादन मोड में लागत बहुत छोटी है। –

+0

यह एक समान तरीके से व्यवहार करना प्रतीत होता था। मुझे प्रोड टूल से लॉग फाइलों से 50 मिलीमीटर मिल गए। हालांकि यह नेट 2 था। – Grzenio

0

मुझे लगता है कि यह थोड़ा परिस्थितिपूर्ण है, लेकिन इससे अधिक नियंत्रित है कि वापसी की घटना वास्तव में एक अपवाद है या नहीं। इंडेक्सऑफ उदाहरण एक पूरी तरह से सामान्य आउटपुट प्रतिक्रिया है जहां -1 एकमात्र चीज है जो समझ में आता है (या रेफ प्रकार के लिए शून्य)।

एक अपवाद बिल्कुल सही होना चाहिए: असाधारण, जिसका अर्थ है कि आप सभी स्टैक ट्रेसिंग और हैंडलिंग चाहते हैं जो आप एक वास्तविक अपवाद से प्राप्त कर सकते हैं।

+0

इंडेक्सऑफ से अपवाद फेंकना -1 से अधिक तार्किक है। लेकिन यह गलत है क्योंकि यह लिखने के लिए सूचकांक का उपयोग कर कोड बनाता है। "अपवाद सिद्धांत" को न देखें, फ़ंक्शन का उपयोग करने वाले कोड को देखें। –

+0

मैं कहां से असहमत हूं, मैं असहमत हूं - इंडेक्स जहां कुछ मिलता है, वह नहीं मिलता है, जो कि नहीं मिला, अप्रत्याशित – annakata

3

मामलों में आपने उल्लेख किया है कि मैं वापसी मूल्य पसंद करूंगा क्योंकि मुझे पता है कि इसके साथ क्या करना है। और एक ही दायरे में पकड़ने के साथ परेशान करने का कोई कारण नहीं है।

बेशक यदि आपका तर्क इस तरह से बनाया गया है कि मूल्य हमेशा सूची में होना चाहिए और उनकी अनुपस्थिति प्रोग्रामर तर्क त्रुटि है - अपवाद हैं।

1

जावा पृष्ठभूमि से आ रहा है, मैं ज्यादातर मामलों में अपवाद पसंद करता हूं। यह आपके कोड को अधिक क्लीनर बनाता है जब इसमें से आधा वापसी मूल्यों की जांच नहीं किया जाता है।

यह कहा गया है कि यह आवृत्ति पर निर्भर करता है कि कुछ "विफलता" के परिणामस्वरूप होने की संभावना है। अपवाद महंगे हो सकते हैं, इसलिए आप उन्हें उन चीजों के लिए अनिवार्य रूप से फेंकना नहीं चाहते हैं जो अक्सर असफल हो जाएंगे।

+0

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

-1

एक अपवाद कुछ "असाधारण" होना चाहिए। तो अगर आप ढूँढें, और आप कुछ ढूंढने की उम्मीद करते हैं, इससे कोई फर्क नहीं पड़ता, तो अगर आपको कुछ अच्छा नहीं लगता तो अपवाद फेंकना अच्छा व्यवहार है।

यदि यह सामान्य प्रवाह है, तो आपको कभी-कभी कुछ नहीं मिलता है, फिर अपवाद फेंकना बुरा होता है।

1

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

उदाहरण

// if this throws an exception 
list.GetByName("Frank") // throws NotFound exception 
// provide a method to test 
list.TryGetByName("Frank") // returns false 

कि जिस तरह से आप की तरह

MyItem item; 
if (list.TryGetByName("Frank")) 
    item = list.GetByName("Frank"); 
+0

धन्यवाद, लेकिन यह 2x धीमी नहीं होगी? –

+0

अच्छा सुझाव। इस तरह आप जिस कॉल का उपयोग करते हैं उसकी पसंद भी मंशा व्यक्त करती है। यदि आप कोशिश करें ..., तो यह आपके कोड के भविष्य के पाठकों को बताता है कि आप जानते हैं * यह असफल हो सकता है। यदि आप सीधे प्राप्त करते हैं, तो यह सुझाव देता है कि आप एक प्राथमिकता हमेशा एक परिणाम होने की उम्मीद करते हैं। – JeffH

+0

@ जोन - आप गति के बारे में सही हैं। इसके आस-पास एक तरीका है कि रिटर्न परिणाम कोड हो और एक तर्क हो जहां उत्तर वापस आता है। आप रिटर्न कोड की जांच करते हैं और यदि यह अच्छा है, तो उत्तर का उपयोग करें। उदाहरण के रूप में http://msdn.microsoft.com/en-us/library/system.int32.tryparse.aspx देखें। – JeffH

6

कुछ अंगूठे का एक नियम अपवाद उपयोग करने के लिए है लिख कर अपवाद को छोड़ सकते हैं केवल जब कुछ होता है के लिए है कि "नहीं करना चाहिए हो "।

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

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

+0

मेरे उपयोग के मामलों के लिए, अधिकांश समय इंडेक्सऑफ वापस नहीं आना चाहिए -1। तो क्या यह एक अपवाद होना चाहिए? नहीं, अपवादों के लिए "बुरा नहीं होना चाहिए" एक बुरा मानदंड है। यह होना चाहिए "उपयोगकर्ता के कोड को आसान बनाता है"। –

3

तुम मेरी दो भाग ब्लॉग श्रृंखला है कि व्यापार गत यहाँ का एक बहुत की चर्चा का आनंद सकता है क्या, अपने प्रोग्रामिंग भाषा का समर्थन करता है सुविधाओं के रूप में यह काफी प्रासंगिक लगती है पर निर्भर करता है:

An example of the interplay between language features and library design, part one

An example of the interplay between language features and library design, part two

मैं जोड़ूंगा कि मुझे लगता है कि इस सवाल के बहुत सारे जवाब खराब हैं (मैंने अपने कई सहवासों को कम किया)। विशेष रूप से खराब

if (ItWillSucceed(...)) { 
    DoIt(...) 
} 

जो सभी प्रकार के दुखी मुद्दों को बनाते हैं (विवरण के लिए मेरा ब्लॉग देखें) के साथ एपीआई हैं।

1

प्रोग्रामिंग यह सब निर्भर करता है से संबंधित कई मुद्दों में के रूप में ...

मैं पहली बार अपने एपीआई इसलिए असाधारण मामलों पहली जगह में ऐसा नहीं हो सकता परिभाषित करने के लिए प्रयास करना चाहिए वास्तव में है कि एक पाते हैं।

अनुबंध द्वारा डिजाइन का उपयोग करने से यह करने में मदद मिल सकती है। यहां कोई ऐसा फ़ंक्शन डालेगा जो त्रुटि या क्रैश फेंक देगा और प्रोग्रामिंग त्रुटि इंगित करेगा (उपयोगकर्ता त्रुटि नहीं)। (कुछ मामलों में इन चेक को रिहाई मोड में हटा दिया जाता है।)

फिर सामान्य जेनरेट विफलताओं के लिए अपवाद रखें, जैसे डीबी कनेक्शन विफल, आशावादी लेनदेन विफल, डिस्क लेखन विफल रहा।

इन अपवादों को तब तक पकड़ा जाना चाहिए जब तक वे 'उपयोगकर्ता' तक नहीं पहुंच जाते। और परिणामस्वरूप उपयोगकर्ता को फिर से प्रयास करने की आवश्यकता होगी।

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

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

transferInternal(int account_id_1, int account_id_2, double amount) 
{ 
    // This is an internal function we require the client to provide us with 
    // valid arguments only. (No error handling done here.) 
    REQUIRE(accountExists(account_id_1)); // Design by contract argument checks. 
    REQUIRE(accountExists(account_id_2)); 
    REQUIRE(balance(account_id_1) > amount); 
    ... do the actual transfer work 
} 

string transfer(int account_id_1, int account_id_2, double amount) 
{ 
    DB.start(); // start transaction 
    string msg; 
    if (!checkAccount(account_id_1, msg)) return msg; // common checking code used from multiple operations. 
    if (!checkAccount(account_id_2, msg)) return msg; 
    if (!checkBalance(account_id_1, amount)) return msg; 
    transferInternal(account_id_1, account_id_2, amount); 
    DB.commit(); // This could fail with an exception if someone else changed the balance while this transaction was active. (Very unlikely but possible) 
    return "cash transfer ok"; 
} 
11

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

तो क्या मैं आमतौर पर करते हैं तय करने के लिए क्या एक समारोह (कि आम तौर पर व्यापार की आवश्यकताओं को या कुछ और से आता है) करना चाहिए, और फिर सब कुछ के लिए अपवाद फेंक है।

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

बेशक

, वहाँ हमेशा अस्पष्ट मामलों में (एक शब्दकोश में नहीं मिला एक कुंजी के साथ) की तरह कर रहे हैं। उन लोगों को दूर और बीच में होना चाहिए, लेकिन वहां आपको केवल अपने सुरुचिपूर्ण समाधान के बारे में महसूस करना होगा।

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

+0

यह निर्भर करता है कि लोअर लेवल अपवादों को पकड़ना आमतौर पर खराब डिजाइन नहीं होता है। आपको उन्हें अन्य अपवादों में लपेटना चाहिए और यूआई को एक अधिक दोस्ताना संदेश के साथ बुलबुला करना चाहिए। – Martin

+0

हम्म, हाँ, मैंने इसके बारे में सोचा नहीं था। –

+0

मेरे पास एक ऐसी वेबसाइट है जहां कोई उपयोगकर्ता प्रोफ़ाइल चित्र को फसल और अपलोड कर सकता है। यदि उपयोगकर्ता एक अमान्य छवि अपलोड करता है, तो क्या यह एक अवैध ImageException फेंकने (और पकड़ने) के लिए अच्छा अभ्यास होगा? –

3

आप किस का उपयोग करेंगे?

एक:

item = list.Find(x); 

बी:

If (list.Contains(x)) 
    item = list.Find(x); 
else 
    item = null; 

सी:

try { 
    item = list.Find(x); 
} 
catch { 
    item = null; 
} 

मैं जवाब शर्त करने को तैयार हूँ ए इसलिए है, यह लौटने मामले डिफ़ॉल्ट में (टी) सही विकल्प है।

+0

रिटर्निंग डिफ़ॉल्ट (टी) का तात्पर्य है कि आप कभी भी 'सामान्य' ऑपरेशन के तहत डिफ़ॉल्ट (टी) वापस नहीं करेंगे। हालांकि इसे संदर्भ प्रकारों के लिए उचित ठहराया जा सकता है, यह मूल्य प्रकारों के लिए आसान नहीं है क्योंकि उदाहरण के लिए शून्य एक आम परिणाम है। आप एक जादू संख्या चुन सकते हैं लेकिन यह वास्तव में स्मार्ट भी नहीं है। –

+0

शून्य (टी) रास्ता हो सकता है। प्रीचैकिंग निश्चित रूप से केवल तभी किया जाना चाहिए जब आप लक्ष्य को नियंत्रित करते हैं और दौड़ की स्थिति को पेश करने का कोई जोखिम नहीं है। इस मामले में मुझे बस पैरामीटर समाधान के साथ खोने वाली संगतता पसंद है। –

+0

मैं किसी भी समय आपसे असहमत नहीं हूं। –

2

बेहतर भाषाएं आपको अपनी आवश्यकताओं के अनुरूप करने देती हैं। स्मालटाक -80 में की तरह:

निम्नलिखित वहाँ आईडी के लिए कोई उपयोगकर्ता है कि अगर एक अपवाद बढ़ा देंगे:

user := userDictionary at: id 

यह एक दिया ब्लॉक एक उच्च स्तरीय समारोह है जो मूल्यांकन करेंगे:

user := userDictionary at: id ifAbsent: [ 
    "no such id, let's return the user named Anonymous" 
    users detect: [ :each | each name = 'Anonymous' ] ] 

कृपया ध्यान दें कि वास्तविक विधि यहां है: ifAbsent :.

0

पहले के लिए, मैं वास्तव में ऐसा नहीं default(T) विकल्प की तरह: क्या आप एक int सूची है, तो जहां 0 (संभवतः इस int के डिफ़ॉल्ट है, मैं का उपयोग नहीं करते सी #) एक पूरी तरह से स्वीकार्य मूल्य है?

(क्षमा याचना जावा वाक्य रचना के लिए, लेकिन अगर मुझे लगता है कि करने के लिए सी # की कोशिश की मैं शायद एक मेस इसके बारे में कहीं बनाना चाहते हैं :))

class Maybe<T> { 
    public Maybe() { 
     set = false; 
     value = null; 
    } 

    public Maybe(T value) { 
     set = true; 
     this.value = value; 
    } 

    public T get(T defaultVal) { 
     if (set) 
      return value; 
     return defaultVal; 
    } 

    private boolean set; 
    private T value; 
} 

तब के पाठ्यक्रम Find एक Maybe<T>, और फोन करने वाले वापसी होगी इस संदर्भ में कुछ मान चुनता है जो एक समझदार डिफ़ॉल्ट है। शायद default(T) सही उत्तर के लिए, एक सुविधाजनक विधि के रूप में आप getDefault जोड़ सकते हैं।

IndexOf के लिए, हालांकि, -1 इस के लिए एक समझदार मूल्य, है के बाद से मान्य मान स्पष्ट रूप से हमेशा> = 0 कर रहे हैं, इसलिए मैं तो बस यही काम था।

0

जो मैं आमतौर पर इस मामले में करता हूं वह एक विकल्प वर्ग (एफ # द्वारा प्रेरित) को परिभाषित करना है और Tryfind() विधि के साथ IENumerable का विस्तार करना है जो विकल्प वर्ग देता है।

public class Option<T> 
{ 
    string _msg = ""; 
    T _item; 

    public bool IsNone 
    { 
     get { return _msg != "" ? true : false; } 
    } 

    public string Msg 
    { 
     get { return _msg; } 
    } 

    internal Option(T item) 
    { 
     this._item = item; 
     this._msg = ""; 
    } 

    internal Option(string msg) 
    { 
     if (String.IsNullOrEmpty(msg)) 
      throw new ArgumentNullException("msg"); 

     this._msg = msg; 
    } 

    internal T Get() 
    { 
     if (this.IsNone) 
      throw new Exception("Cannot call Get on a NONE instance."); 

     return this._item; 
    } 

    public override string ToString() 
    { 
     if (this.IsNone) 
      return String.Format("IsNone : {0}, {1}", this.IsNone, typeof(T).Name); 

     else 
      return String.Format("IsNone : {0}, {1}, {2}", this.IsNone, typeof(T).Name, this._item.ToString()); 
    } 

}

तो फिर तुम यह रूप में

var optionItem = list.TryFind(x => trueorFalseTest()); 
if (!optionItem.IsNone) 
    var myItem = optionItem.Get(); 

उपयोग कर सकते हैं इस तरह, कि क्या आइटम मौजूद है या नहीं, संग्रह केवल एक बार चल रहा है।

संबंधित मुद्दे