2009-03-06 5 views
16

मान लें कि हमारे पास एक ऐसी विधि है जो गणना के मूल्य को स्वीकार करती है। इस विधि के बाद यह मानता है कि मान वैध है, यह संभावित मानों पर switch es है। तो सवाल यह है कि के बाद अप्रत्याशित मानों को संभालने का पसंदीदा तरीका क्या है? मान सीमा को सत्यापित किया गया है?अप्रत्याशित enum मूल्यों को संभालने के लिए पसंदीदा तरीका क्या है?

enum Mood { Happy, Sad } 

public void PrintMood(Mood mood) 
{ 
    if (!Enum.IsDefined(typeof(Mood), mood)) 
    { 
     throw new ArgumentOutOfRangeException("mood"); 
    } 

    switch (mood) 
    { 
     case Happy: Console.WriteLine("I am happy"); break; 
     case Sad: Console.WriteLine("I am sad"); break; 
     default: // what should we do here? 
    } 

default मामले से निपटने का पसंदीदा तरीका क्या है:

उदाहरण के लिए

?

  • // can never happen
  • Debug.Fail() (या Debug.Assert(false))
  • throw new NotImplementedException() की तरह एक टिप्पणी (या किसी अन्य अपवाद)
  • कुछ अन्य तरह से मैं
+0

मैं एक उत्तर पोस्ट कर रहा हूं मेरी राय के साथ। यदि आपके पास कोई अलग राय है, या एक अलग मामला जो इसका समर्थन करता है, तो कृपया अपना ज्ञान साझा करें। प्रश्न का उद्देश्य इस स्थिति का सामना करने वाले अन्य लोगों (और मुझे) की सहायता के लिए विषय के बारे में विचार इकट्ठा करना है। –

+0

इसे जावा के रूप में क्यों चिह्नित किया गया था? – OscarRyz

+0

@ ऑस्कर: यह एक भाषा-स्वतंत्र प्रश्न IMHO होना चाहिए। लोग यहां दोनों भाषाओं के लिए जवाब पा सकते हैं। –

उत्तर

10

नहीं सोचा मैं करना पसंद करते हैं छोड़ दो throw new NotImplementedException("Unhandled Mood: " + mood)। मुद्दा यह है कि भविष्य में गणना बदल सकती है, और इस विधि को तदनुसार अपडेट नहीं किया जा सकता है। अपवाद फेंकना सबसे सुरक्षित तरीका प्रतीत होता है।

मुझे Debug.Fail() विधि पसंद नहीं है, क्योंकि विधि लाइब्रेरी का हिस्सा हो सकती है, और नए मानों को डीबग मोड में परीक्षण नहीं किया जा सकता है। उस लाइब्रेरी का उपयोग करने वाले अन्य अनुप्रयोगों को उस मामले में अजीब रनटाइम व्यवहार का सामना करना पड़ सकता है, जबकि अपवाद फेंकने के मामले में त्रुटि तुरंत जानी जाएगी।

नोट: NotImplementedExceptioncommons.lang में मौजूद है।

+0

इसे आपके मूल पोस्ट में संपादित किया जाना चाहिए था। –

+1

@ एसएनओफफस: क्यों? यह एक बिल्कुल वैध जवाब है। –

+0

जैसा कि मैंने अपने उत्तर में कहा था, यह मान्य हो सकता है, लेकिन यह गलत है ओओ ... –

1

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

7

जावा में, मानक तरीका दो कारणों के लिए, एक AssertionError फेंकने के लिए है:

  1. यह सुनिश्चित करता है कि भले ही asserts अक्षम हैं, एक त्रुटि फेंक दिया है।
  2. आप जोर दे रहे हैं कि कोई अन्य enum मान नहीं हैं, इसलिए AssertionError आपकी धारणा NotImplementedException से बेहतर है (जो जावा वैसे भी नहीं है)।
+0

लेकिन AssertionError java.lang.Error को बढ़ाता है, java.lang.Exception नहीं। इसका मतलब यह होगा कि कोड में अन्य परतों को अपवाद को पकड़ने और इससे पुनर्प्राप्त करने का मौका नहीं होगा। (तकनीकी रूप से वे बोल सकते हैं, लेकिन व्यावहारिक रूप से अधिकांश लोग java.lang.Error को पकड़ नहीं सकते हैं (और नहीं)। –

+0

यदि आप पहले ही जांच चुके हैं कि यह वैध है, तो AssertionError कभी नहीं होना चाहिए (यानी, आखिरकार, क्या आप जोर दे रहे हैं)। यदि ऐसा होता है, तो चीजें इतनी गलत होती हैं कि मुझे लगता है कि इसे पकड़ने की कोशिश करना व्यर्थ होगा। लेकिन मुझे लगता है कि कभी-कभी इसे पकड़ने के लिए उपयोगी हो सकता है। –

+0

इस प्रश्न में भी चर्चा देखें: http://stackoverflow.com/questions/398953/java-what-is-the-preferred-throwable-to-use-in-a-private-utility-class-construct –

2

मेरी कोड बेस में काफी हर स्विच बयान के लिए, मैं निम्न डिफ़ॉल्ट मामले

switch(value) { 
... 
default: 
    Contract.InvalidEnumValue(value); 
} 

विधि एक अपवाद बिंदु एक त्रुटि का पता चला था पर enum का मूल्य का ब्यौरा फेंक देंगे।

public static void InvalidEnumValue<T>(T value) where T: struct 
{ 
    ThrowIfFalse(typeof(T).IsEnum, "Expected an enum type"); 
    Violation("Invalid Enum value of Type {0} : {1}", new object[] { typeof(T).Name, value }); 
} 
+0

लेकिन मान मान्य हो सकता है, भले ही विधि को प्रतिबिंबित करने के लिए अद्यतन नहीं किया गया हो। मुझे लगता है कि "अमान्य मान" संदेश यहां थोड़ा भ्रामक है। –

+0

@ होसम, लेकिन कोड उस मूल्य की अपेक्षा नहीं कर रहा था, इसलिए मैंने नाम चुना है। "अप्रत्याशित मूल्य" जैसा कि मुझे लगता है उतना ही काम करेगा, लेकिन यह कैप्चर करना है कि मूल्य वास्तव में नहीं है कि प्रोग्रामर का उद्देश्य – JaredPar

+0

@ जेरेडपायर: पर्याप्त मेला है। मैं "अनचाहे मूल्य" की तर्ज पर और सोच रहा था, जो यह स्पष्ट करता है कि यह एक अनचाहे मामला है (आईएमएचओ)। –

0

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

यदि आपके कार्य को या तो मेरे मामले में एक अप्रत्याशित मूल्य या अपरिभाषित हो रहा है, तो एक सामान्य उत्तर संभव नहीं लगता है। मेरे लिए, यह enum मान का मूल्यांकन करने के कारण के संदर्भ पर निर्भर करता है: क्या यह ऐसी स्थिति है जहां कोड निष्पादन रोकना चाहिए, या डिफ़ॉल्ट मान का उपयोग किया जा सकता है?

3

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

0

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

+0

मैं पूरी तरह से सहमत हूं, लेकिन वास्तविक दुनिया में, enum अद्यतन होने पर कुछ विधियों को याद किया जा सकता है, यही कारण है कि मैं सवाल पूछ रहा हूं। ऐसे मामलों में आप क्या करते हैं? –

3

सही कार्यक्रम प्रतिक्रिया हो एक तरह से है कि डेवलपर को आसानी से समस्या दिखाई देती है के लिए अनुमति देगा में मरने के लिए होगा। mmyers और JaredPar दोनों ने ऐसा करने के अच्छे तरीके दिए।

क्यों मरते हैं? यह इतना चरम लगता है!

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

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

+0

हाँ, यह बहुत चरम लगता है! एक अपवाद फेंकना पर्याप्त नहीं होगा, इस अर्थ में कि एक त्रुटि त्रुटि से ठीक हो सकती है? क्या होगा यदि विधि बहुत महत्वपूर्ण नहीं है? विफलता के मामले अभी भी क्यूए में कब्जा कर लिया जाएगा, क्योंकि वे उम्मीद से अलग परिणाम देंगे। –

+0

बढ़िया, आप अपनी गलती से ठीक हो जाते हैं, लेकिन अब आप नहीं जानते कि आपका प्रोग्राम किस स्थिति में है। जैसा कि मैंने कहा है, इससे अन्य सूक्ष्म समस्याओं का कारण बन जाएगा। क्यूए उन सूक्ष्म त्रुटियों को देखेगा और उन्हें रिपोर्ट करेगा, जबकि समस्या की जड़ को संबोधित नहीं किया जाएगा। –

+0

यह * मुझे * त्रुटि से ठीक नहीं होता है, लेकिन कॉल पदानुक्रम में उच्च परतें। यह एक महत्वपूर्ण भेद IMHO है। अगर त्रुटि लॉग (जो होना चाहिए), एक अच्छा त्रुटि संदेश सीधे मूल कारण के लिए नेतृत्व करेंगे। –

0

मैं आमतौर पर अपरिभाषित (0) मूल्य परिभाषित करने की कोशिश:

enum Mood { Undefined = 0, Happy, Sad } 

इस तरह मैं हमेशा कह सकते हैं:

switch (mood) 
{ 
    case Happy: Console.WriteLine("I am happy"); break; 
    case Sad: Console.WriteLine("I am sad"); break; 
    case Undefined: // handle undefined case 
    default: // at this point it is obvious that there is an unknown problem 
      // -> throw -> die :-) 
} 

यह कम से कम है कि मैं कितना आमतौर पर यह करते हैं।

+0

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

+1

और डिफ़ॉल्ट मामले को अभी भी संभालना होगा, और यह सवाल का मुद्दा है। –

+0

ठीक है, मैंने टिप्पणी में उल्लेख किया - फेंक और मर जाओ। मैं अपवाद वर्ग से प्राप्त अपना अपवाद फेंक दूंगा। –

1

यह उन प्रश्नों में से एक है जो साबित करता है कि परीक्षण संचालित विकास इतना महत्वपूर्ण क्यों है।

इस मामले में मैं एक NotSupportedException के लिए जाना चाहूंगा क्योंकि शाब्दिक रूप से मान अनचाहे था और इसलिए समर्थित नहीं था। एक NotImplementedException के बारे में अधिक विचार देता है: "यह अभी तक समाप्त नहीं हुआ है";)

कॉलिंग कोड इस तरह की स्थिति को संभालने में सक्षम होना चाहिए और इस तरह की स्थितियों का आसानी से परीक्षण करने के लिए यूनिट परीक्षण बनाए जा सकते हैं।

+0

यह एक अच्छा मुद्दा है। लेकिन कभी-कभी आप वैध मानों के लिए NotSupportedException फेंकते हैं, जैसे FileMode.Write के साथ StreamReader बनाते समय। मैं सोच रहा था कि चर्चा की जा रही स्थिति काफी अलग है कि इसे अन्य मामलों की तरह संभाला नहीं जाना चाहिए। –

2

सी # के लिए, कुछ मूल्यवान यह है कि Enum.IsDefined() is dangerous है। आप इस पर भरोसा नहीं कर सकते जैसे आप हैं। अपेक्षित मूल्यों में से कुछ प्राप्त करना अपवाद फेंकने और जोर से मरने का एक अच्छा मामला है।

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

+0

मैं निश्चित रूप से सी # के लिए जावा enums पसंद करते हैं। क्या आप अपरिचित मूल्यों के मामले में जो अपवाद फेंक देंगे, उस पर विस्तार कर सकते हैं? –

+0

जावा में मैं एक IllegalStateException फेंक दूंगा अगर मुझे एक enum मान मिला जो मुझे पहचान नहीं आया। IllegalArgumentException ठीक है लेकिन मेरे लिए यह अवैध राज्य की तरह लगता है। – cletus

11

मुझे लगता है कि उपर्युक्त उत्तरों में से अधिकांश मान्य हैं, लेकिन मुझे यकीन नहीं है कि कोई भी सही है।

सही उत्तर यह है कि, आप शायद ही कभी ओओ भाषा में स्विच करते हैं, यह इंगित करता है कि आप अपना ओओ गलत कर रहे हैं। इस मामले में, यह एक सही संकेत है कि आपके एनम कक्षा में समस्याएं हैं।

आपको केवल कंसोल बुलाया जाना चाहिए। राइटलाइन (mood.moodMessage()), और प्रत्येक राज्य के लिए मनोदशा को परिभाषित करना।

यदि कोई नया राज्य जोड़ा जाता है - आपका सभी कोड स्वचालित रूप से अनुकूल होना चाहिए, कुछ भी विफल नहीं होना चाहिए, अपवाद फेंकना चाहिए या परिवर्तनों की आवश्यकता है।

संपादित करें: टिप्पणी का जवाब।

आपके उदाहरण में, "अच्छा ओओ" होने के लिए फ़ाइल मोड की कार्यक्षमता को फ़ाइल मोड ऑब्जेक्ट द्वारा नियंत्रित किया जाएगा। इसमें "ओपन, रीड, राइट ..." ऑपरेशंस के साथ एक प्रतिनिधि ऑब्जेक्ट हो सकता है जो प्रत्येक फ़ाइलमोड के लिए अलग-अलग होते हैं, इसलिए File.open ("name", FileMode.Create) को कार्यान्वित किया जा सकता है (परिचितता की कमी के बारे में खेद है एपीआई):

open(String name, FileMode mode) { 
    // May throw an exception if, for instance, mode is Open and file doesn't exist 
    // May also create the file depending on Mode 
    FileHandle fh = mode.getHandle(name); 
    ... code to actually open fh here... 
    // Let Truncate and append do their special handling 
    mode.setPosition(fh); 
} 

यह स्विच के साथ यह करने के लिए ... (वैसे कोशिश कर रहा से बहुत neater है, विधियों दोनों पैकेज-निजी और संभवतः प्रत्यायोजित करने के लिए "मोड" वर्ग)

होगा

जब ओओ अच्छी तरह से किया जाता है, तो प्रत्येक विधि वास्तव में समझने योग्य, सरल कोड की कुछ पंक्तियों की तरह दिखती है - सरल सरल। आपको हमेशा यह महसूस होता है कि कुछ छोटी गन्दा "पनीर न्यूक्लियस" सभी छोटी नाचो वस्तुओं को एक साथ रखती है, लेकिन आप इसे कभी नहीं ढूंढ सकते हैं - यह सभी तरह से नाचो है ...

+0

बस बाकी सब कुछ की तरह, जब वे सही तरीके से उपयोग किए जाते हैं तो enums अच्छे होते हैं। उदाहरण के लिए, यदि मैं फ़ाइल हैंडलिंग के लिए एक एपीआई डिज़ाइन कर रहा हूं, तो मैं 'File.Open (स्ट्रिंग फ़ाइल नाम, फ़ाइल मोड मोड)' कहूंगा, जहां फ़ाइलमोड एक साधारण मूल्य enum है, जिसमें कोई व्यवहार संलग्न नहीं है। मुझे यह "ओओ गलत करना" नहीं दिख रहा है। –

+2

वाह, मैंने बुराई के बारे में कुछ भी नहीं कहा बुरा! मुझे टाइपएफ़ गणनाएं पसंद हैं! - और आपके द्वारा निर्दिष्ट उपयोग बहुत अच्छा है। यह स्विच स्टेटमेंट है जो समस्याग्रस्त है। यह इंगित करता है कि एक व्यवहार enum में स्थानांतरित किया जाना चाहिए। –

+0

क्षमा करें अगर मुझे गलत समझा गया है, लेकिन सवाल अभी भी बना हुआ है: आप स्विच स्टेटमेंट के बिना 'File.Open' को कैसे कार्यान्वित करते हैं? अपने बारे में बात करते हुए, मैं फ़ाइलमोड ऑब्जेक्ट को फ़ाइल खोलने के लिए एक विधि नहीं दूंगा, क्योंकि यह बस वहां से संबंधित नहीं है। आप एक विकल्प के रूप में क्या सुझाव देते हैं? –

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