`गोटो` वूडू से परहेज?


47

मेरे पास एक switchसंरचना है जिसे संभालने के लिए कई मामले हैं। switchएक से अधिक चल रही है enumजो संयुक्त मूल्यों के माध्यम से डुप्लिकेट कोड के मुद्दे बन गया है:

// All possible combinations of One - Eight.
public enum ExampleEnum {
    One,
    Two, TwoOne,
    Three, ThreeOne, ThreeTwo, ThreeOneTwo,
    Four, FourOne, FourTwo, FourThree, FourOneTwo, FourOneThree,
          FourTwoThree, FourOneTwoThree
    // ETC.
}

वर्तमान में switchसंरचना प्रत्येक मूल्य को अलग से संभालती है:

// All possible combinations of One - Eight.
switch (enumValue) {
    case One: DrawOne; break;
    case Two: DrawTwo; break;
    case TwoOne:
        DrawOne;
        DrawTwo;
        break;
     case Three: DrawThree; break;
     ...
}

आपको वहां विचार मिलता है। मैंने वर्तमान में ifइसके बजाय एक एकल पंक्ति के साथ संयोजन को संभालने के लिए एक खड़ी संरचना में तोड़ दिया है :

// All possible combinations of One - Eight.
if (One || TwoOne || ThreeOne || ThreeOneTwo)
    DrawOne;
if (Two || TwoOne || ThreeTwo || ThreeOneTwo)
    DrawTwo;
if (Three || ThreeOne || ThreeTwo || ThreeOneTwo)
    DrawThree;

यह अविश्वसनीय रूप से लंबे तार्किक मूल्यांकन के मुद्दे को प्रस्तुत करता है जो पढ़ने के लिए भ्रमित कर रहे हैं और बनाए रखने में मुश्किल हैं। इसे बाहर करने के बाद मैंने विकल्पों के बारे में सोचना शुरू किया और switchमामलों के बीच में एक संरचना के विचार के बारे में सोचा ।

मुझे gotoउस मामले में उपयोग करना होगा क्योंकि C#गिरावट की अनुमति नहीं है। हालाँकि, यह switchसंरचना में इधर-उधर कूदने के बावजूद अविश्वसनीय रूप से लंबी तर्क श्रृंखला को रोकता है , और यह अभी भी कोड दोहराव में लाता है।

switch (enumVal) {
    case ThreeOneTwo: DrawThree; goto case TwoOne;
    case ThreeTwo: DrawThree; goto case Two;
    case ThreeOne: DrawThree; goto default;
    case TwoOne: DrawTwo; goto default;
    case Two: DrawTwo; break;
    default: DrawOne; break;
}

यह अभी भी एक साफ पर्याप्त समाधान नहीं है और उस gotoकीवर्ड से जुड़ा एक कलंक है जिससे मैं बचना चाहूंगा। मुझे यकीन है कि इसे साफ करने का एक बेहतर तरीका होना चाहिए।


मेरा प्रश्न

क्या पठनीयता और स्थिरता को प्रभावित किए बिना इस विशिष्ट मामले को संभालने का एक बेहतर तरीका है?


28
ऐसा लगता है कि आप एक बड़ी बहस करना चाहते हैं। लेकिन आपको गोटो का उपयोग करने के लिए एक अच्छे मामले के बेहतर उदाहरण की आवश्यकता होगी। झंडा enum बड़े करीने से इसे हल करता है, भले ही आपका उदाहरण यदि कथन स्वीकार्य नहीं है और यह मुझे ठीक लगता है
Evan

5
@ ईवन कोई भी गोटो का उपयोग नहीं करता है। लिनक्स कर्नेल स्रोत कोड को आपने आखिरी बार कब देखा था?
जॉन डौमा

6
आप का उपयोग gotoतब करते हैं जब आपकी भाषा में उच्च स्तरीय संरचना मौजूद नहीं होती है। मैं कभी-कभी चाहता था कि ए fallthru; कीवर्ड gotoलेकिन ओह अच्छी तरह से उस विशेष उपयोग से छुटकारा पाने के लिए ।
जोशुआ

25
सामान्य शब्दों में, यदि आपको gotoC # जैसी उच्च-स्तरीय भाषा में एक का उपयोग करने की आवश्यकता महसूस होती है , तो संभवतः आपने कई अन्य (और बेहतर) डिज़ाइन और / या कार्यान्वयन विकल्पों की अनदेखी की है। अति हतोत्साहित।
code_dredd

11
C # में "गोटो केस" का उपयोग करना अधिक सामान्य "गोटो" का उपयोग करने से अलग है, क्योंकि यह है कि आप स्पष्ट गिरावट को कैसे पूरा करते हैं।
हैमराइट

जवाबों:


175

मुझे gotoकथनों के साथ पढ़ने के लिए कोड कठिन लगता है । मैं आपके enumअलग तरीके से संरचना की सिफारिश करूंगा । उदाहरण के लिए, यदि आपका enumएक बिटफील्ड था जहां प्रत्येक बिट में से किसी एक विकल्प का प्रतिनिधित्व किया गया था , तो यह इस तरह दिख सकता है:

[Flags]
public enum ExampleEnum {
    One = 0b0001,
    Two = 0b0010,
    Three = 0b0100
};

झंडे की विशेषता कंपाइलर को बताती है कि आप उन मूल्यों को स्थापित कर रहे हैं जो ओवरलैप नहीं हैं। इस कोड को कॉल करने वाला कोड उपयुक्त बिट सेट कर सकता है। फिर आप कुछ ऐसा कर सकते हैं जिससे यह स्पष्ट हो सके कि क्या हो रहा है:

if (myEnum.HasFlag(ExampleEnum.One))
{
    CallOne();
}
if (myEnum.HasFlag(ExampleEnum.Two))
{
    CallTwo();
}
if (myEnum.HasFlag(ExampleEnum.Three))
{
    CallThree();
}

इसके लिए कोड की आवश्यकता होती है myEnumजो बिटफिल्ड्स को ठीक से सेट करने के लिए सेट करता है और झंडे विशेषता के साथ चिह्नित होता है। लेकिन आप अपने उदाहरणों में एनमों के मूल्यों को बदलकर ऐसा कर सकते हैं:

[Flags]
public enum ExampleEnum {
    One = 0b0001,
    Two = 0b0010,
    Three = 0b0100,
    OneAndTwo = One | Two,
    OneAndThree = One | Three,
    TwoAndThree = Two | Three
};

जब आप प्रपत्र में एक संख्या लिखते हैं 0bxxxx, तो आप इसे बाइनरी में निर्दिष्ट कर रहे हैं। तो आप देख सकते हैं कि हमने बिट 1, 2, या 3 (अच्छी तरह से, तकनीकी रूप से 0, 1, या 2 सेट किया है, लेकिन आपको यह विचार मिलता है)। यदि आप संयोजन अक्सर एक साथ सेट किए जा सकते हैं, तो आप बिटवाइज़ का उपयोग करके भी संयोजन का नाम दे सकते हैं।


5
ऐसा प्रतीत होता है ! लेकिन यह C # 7.0 या बाद का होना चाहिए, मुझे लगता है।
user1118321

31
वैसे भी, कोई इसे इस तरह भी कर सकता है public enum ExampleEnum { One = 1 << 0, Two = 1 << 1, Three = 1 << 2, OneAndTwo = One | Two, OneAndThree = One | Three, TwoAndThree = Two | Three };:। C # 7 + पर जोर देने की आवश्यकता नहीं है।
डेडुप्लिकेटर

8
आप Enum.HasFlag
IMil

13
[Flags]विशेषता संकलक के लिए कुछ भी संकेत नहीं है। यही कारण है कि आपको अभी भी स्पष्ट रूप से 2 की शक्तियों के रूप में एनम मूल्यों की घोषणा करनी है
जो

19
@UKMonkey मैं असहमत असहमत हूँ। HasFlagप्रदर्शन के लिए एक वर्णनात्मक और स्पष्ट शब्द है और कार्यक्षमता से कार्यान्वयन को रोक देता है। का उपयोग करते हुए &, क्योंकि यह अन्य भाषाओं में आम है एक का उपयोग कर से अधिक नहीं समझ में आता है byteप्रकार के बजाय एक की घोषणा enum
बीजे मायर्स

136

IMO समस्या की जड़ यह है कि कोड का यह टुकड़ा भी मौजूद नहीं होना चाहिए।

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

कॉल को उन तीन फ़ंक्शंस में रखें, जहाँ से संबंधित (यानी जहाँ आपको कार्यों को करने की आवश्यकता है) और इन उदाहरणों में कोड को रीसायकल बिन में कंसाइन करें।

यदि दस ध्वज और क्रियाएं तीन नहीं थीं, तो क्या आप 1024 विभिन्न संयोजनों को संभालने के लिए इस प्रकार के कोड का विस्तार करेंगे? मुझे आशा नहीं है! यदि 1024 बहुत अधिक है, तो 8 भी बहुत सारे हैं, उसी कारण से।


10
तथ्य के रूप में, कई अच्छी तरह से डिज़ाइन किए गए एपीआई हैं जिनमें सभी प्रकार के झंडे, विकल्प और मिश्रित अन्य पैरामीटर हैं। कुछ को पारित किया जा सकता है, कुछ पर प्रत्यक्ष प्रभाव पड़ता है, और फिर भी अन्य को संभवतः एसआरपी को तोड़ने के बिना अनदेखा किया जा सकता है। वैसे भी, समारोह कुछ बाहरी इनपुट को डिकोड कर सकता है, कौन जानता है? इसके अलावा, reductio ad absurdum अक्सर बेतुके परिणामों की ओर ले जाता है, खासकर अगर शुरुआती बिंदु भी वह सब ठोस नहीं है।
Deduplicator

9
इस जवाब को उकेरा। यदि आप सिर्फ GOTO पर बहस करना चाहते हैं, तो यह आपके द्वारा पूछे गए सवाल से अलग सवाल है। प्रस्तुत साक्ष्यों के आधार पर, आपके पास तीन असंगत आवश्यकताएं हैं, जिन्हें एकत्र नहीं किया जाना चाहिए। एक एसई दृष्टिकोण से, मैं प्रस्तुत करता हूं कि एलेफ़ेज़ो सही उत्तर है।

8
@ डेडप्लिकेटर 1,2 और 3 के सभी संयोजनों को नहीं बल्कि कुछ के इस मिश्माश को देखें तो पता चलता है कि यह "अच्छी तरह से डिज़ाइन किया गया एपीआई" नहीं है।
user949300

4
इतना तो। अन्य उत्तर वास्तव में इस सवाल पर सुधार नहीं करते हैं। इस कोड को फिर से लिखने की आवश्यकता है। अधिक फ़ंक्शन लिखने में कुछ भी गलत नहीं है।
only_pro

3
मुझे यह उत्तर पसंद है, विशेष रूप से "अगर 1024 बहुत अधिक है, तो 8 भी बहुत सारे हैं"। लेकिन यह अनिवार्य रूप से "बहुरूपता का उपयोग" है, है ना? और मेरे (कमज़ोर) जवाब को कहने के लिए बहुत बकवास हो रहा है। क्या मैं आपके पीआर व्यक्ति को काम पर रख सकता हूँ? :-)
user949300

29

कभी gotos का उपयोग में से एक है "बच्चों को झूठ" कंप्यूटर विज्ञान की अवधारणाओं। यह समय की 99% सही सलाह है, और यह समय इतना दुर्लभ और विशिष्ट नहीं है कि यह हर किसी के लिए कहीं बेहतर है अगर इसे नए कोडर्स को समझाया जाए क्योंकि "उनका उपयोग न करें"।

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

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

* - IIRC, टेबल या अन्य असंरचित नियंत्रण कथनों का सहारा लिए बिना LALL व्याकरणों को पूरी तरह से पुनरावर्ती-वंश में लागू करना संभव है, इसलिए यदि आप सही मायने में विरोधी हैं, तो यह एक तरीका है।


अब अगला सवाल है, "क्या यह उदाहरण उन मामलों में से एक है?"

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

शेष लोगों में से एक ("तीन") केवल एक ही स्थान से शाखाबद्ध है जिसे मैं देख सकता हूं। तो आप इससे उसी तरह से छुटकारा पा सकते हैं। आप इस तरह दिखने वाले कोड को समाप्त करेंगे:

switch (exampleValue) {
    case OneAndTwo: i += 3 break;
    case OneAndThree: i += 4 break;
    case Two: i += 2 break;
    case TwoAndThree: i += 5 break;
    case Three: i += 3 break;
    default: i++ break;
}

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


2
@PerpetualJ - ठीक है, मुझे निश्चित रूप से खुशी है कि आपने कुछ क्लीनर पाया। यह अभी भी दिलचस्प हो सकता है, अगर यह वास्तव में आपकी स्थिति है, तो इसे गोटो (कोई मामला या शत्रु नहीं है। बस ब्लॉक और गोटो को लेबल किया गया है) के साथ प्रयास करें और देखें कि वे पठनीयता में कैसे तुलना करते हैं। यह कहा जा रहा है, "गोटो" विकल्प को केवल अधिक पठनीय होना जरूरी नहीं है, लेकिन अन्य डेवलपर्स से तर्क और हैम-हैंडेड "प्रयासों" को रोकने के लिए पर्याप्त पठनीय है, इसलिए ऐसा लगता है कि आपको सबसे अच्छा विकल्प मिल गया है।
TED

4
मुझे विश्वास है कि अगर "आगे रखरखाव को राज्यों को जोड़ने या जटिल करने की संभावना है" तो आपको "गोटो का उपयोग करना बेहतर होगा"। क्या आप वास्तव में दावा कर रहे हैं कि संरचित कोड की तुलना में स्पेगेटी कोड को बनाए रखना आसान है? यह ~ 40 साल के अभ्यास के खिलाफ जाता है।
user949300

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

8
@ user949300 - फिर से, "~ 40 साल के अभ्यास" बिल्डिंग कंपाइलर से पता चलता है कि गोटो वास्तव में उस समस्या डोमेन के कुछ हिस्सों के लिए सबसे अच्छा तरीका है, यही कारण है कि यदि आप संकलक स्रोत कोड को देखते हैं, तो आप लगभग हमेशा गोटो पाएंगे।
TED

3
@ user949300 स्पेगेटी कोड विशिष्ट कार्यों या सम्मेलनों का उपयोग करते समय स्वाभाविक रूप से प्रकट नहीं होता है। यह किसी भी समय किसी भी भाषा, कार्य या सम्मेलन में दिखाई दे सकता है। इसका कारण यह है कि भाषा, फ़ंक्शन, या किसी अनुप्रयोग में कन्वेंशन का उपयोग कर रहा है जिसका यह उद्देश्य नहीं था और इसे खींचने के लिए इसे पीछे की तरफ झुकना पड़ता है। हमेशा नौकरी के लिए सही उपकरण का उपयोग करें, और हाँ, कभी-कभी उपयोग करने का मतलब है goto
अबियोना 47

26

सबसे अच्छा जवाब बहुरूपता का उपयोग है

एक अन्य जवाब, जो, IMO, अगर सामान को स्पष्ट और यकीनन छोटा बनाता है :

if (One || OneAndTwo || OneAndThree)
  CallOne();
if (Two || OneAndTwo || TwoAndThree)
  CallTwo();
if (Three || OneAndThree || TwoAndThree)
  CallThree();

goto शायद यहाँ मेरी 58 वीं पसंद है ...


5
बहुरूपता का सुझाव देने के लिए +1, हालांकि आदर्श रूप से ग्राहक कोड को केवल "कॉल ()" तक सरल किया जा सकता है, यह बताने का उपयोग न करें - निर्णय लेने वाले ग्राहक से दूर और कक्षा तंत्र और पदानुक्रम में तर्क को धक्का देने के लिए।
एरिक इद्दत

12
किसी भी चीज़ को सर्वश्रेष्ठ दिखने वाली अनदेखी मानना ​​निश्चित रूप से बहुत बहादुर है। लेकिन अतिरिक्त जानकारी के बिना, मैं थोड़ा संदेह और खुले दिमाग रखने का सुझाव देता हूं। शायद यह दहनशील विस्फोट से ग्रस्त है?
डेडुप्लिकेटर

वाह, मुझे लगता है कि यह पहली बार है जब मैंने कभी बहुरूपता का सुझाव देने के लिए अपमानित किया है। :-)
user949300

25
कुछ लोग, जब एक समस्या का सामना करते हैं, तो कहते हैं "मैं सिर्फ बहुरूपता का उपयोग करूंगा"। अब उन्हें दो समस्याएं हैं, और बहुरूपता।
मोनिका

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

10

ऐसा क्यों नहीं:

public enum ExampleEnum {
    One = 0, // Why not?
    OneAndTwo,
    OneAndThree,
    Two,
    TwoAndThree,
    Three
}
int[] COUNTS = { 1, 3, 4, 2, 5, 3 }; // Whatever

int ComputeExampleValue(int i, ExampleEnum exampleValue) {
    return i + COUNTS[(int)exampleValue];
}

ठीक है, मैं सहमत हूं, यह हैकिश है (मैं C # डेवलपर btw नहीं हूं, इसलिए मुझे कोड के लिए क्षमा करें), लेकिन दक्षता के दृष्टिकोण से यह होना चाहिए? एरे इंडेक्स के रूप में एनम का उपयोग करना मान्य है C #।


3
हाँ। डेटा के साथ कोड को प्रतिस्थापित करने का एक और उदाहरण (जो आमतौर पर गति, संरचना और स्थिरता के लिए वांछनीय है)।
पीटर - मोनिका

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

मेरे पास C # कंपाइलर तक पहुंच नहीं है, लेकिन शायद इस तरह के कोड का उपयोग करके कोड को सुरक्षित बनाया जा सकता है int[ExempleEnum.length] COUNTS = { 1, 3, 4, 2, 5, 3 };:?
लॉरेंट ग्रेजायर

7

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

int ComputeExampleValue(int i, ExampleEnum exampleValue) {
    switch (exampleValue) {
        case One: return i + 1;
        case OneAndTwo: return ComputeExampleValue(i + 2, ExampleEnum.One);
        case OneAndThree: return ComputeExampleValue(i + 3, ExampleEnum.One);
        case Two: return i + 2;
        case TwoAndThree: return ComputeExampleValue(i + 2, ExampleEnum.Three);
        case Three: return i + 3;
   }
}

यह एक अनूठा समाधान है! +1 मुझे नहीं लगता कि मुझे इसे इस तरह से करने की ज़रूरत है, लेकिन भविष्य के पाठकों के लिए बहुत अच्छा है!
PerpetualJ

2

स्वीकृत समाधान ठीक है और आपकी समस्या का एक ठोस समाधान है। हालांकि, मैं एक वैकल्पिक, अधिक सार समाधान प्रस्तुत करना चाहूंगा।

मेरे अनुभव में, तर्क के प्रवाह को परिभाषित करने के लिए enums का उपयोग एक कोड गंध है क्योंकि यह अक्सर खराब वर्ग डिजाइन का संकेत है।

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

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

आइए प्रश्न में प्रस्तुत समस्या पर विचार करें। मुझे वास्तव में यहाँ संदर्भ नहीं पता है, या यह एनम क्या दर्शाता है। यह कार्ड ड्राइंग है? चित्र बनाना? खून निकालना? क्या आदेश महत्वपूर्ण है? मुझे यह भी पता नहीं है कि प्रदर्शन कितना महत्वपूर्ण है। यदि प्रदर्शन या स्मृति महत्वपूर्ण है, तो यह समाधान संभवतः वह नहीं है जो आप चाहते हैं।

किसी भी मामले में, आइए एनम पर विचार करें:

// All possible combinations of One - Eight.
public enum ExampleEnum {
    One,
    Two,
    TwoOne,
    Three,
    ThreeOne,
    ThreeTwo,
    ThreeOneTwo
}

हमारे यहां जो कुछ भी हैं वे कई अलग-अलग Enum मान हैं जो विभिन्न व्यावसायिक अवधारणाओं का प्रतिनिधित्व कर रहे हैं।

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

आइए हम निम्नलिखित इंटरफ़ेस पर विचार करें:

public interface IExample
{
  void Draw();
}

हम इसे अमूर्त वर्ग के रूप में लागू कर सकते हैं:

public abstract class ExampleClassBase : IExample
{
  public abstract void Draw();
  // other common functionality defined here
}

ड्राइंग एक, दो और तीन का प्रतिनिधित्व करने के लिए हमारे पास एक ठोस वर्ग हो सकता है (जो तर्क के लिए अलग तर्क है)। ये संभवतः ऊपर परिभाषित बेस क्लास का उपयोग कर सकते हैं, लेकिन मैं मान रहा हूं कि ड्राऑन अवधारणा एनम द्वारा प्रस्तुत अवधारणा से अलग है:

public class DrawOne
{
  public void Draw()
  {
    // Drawing logic here
  }
}

public class DrawTwo
{
  public void Draw()
  {
    // Drawing two logic here
  }
}

public class DrawThree
{
  public void Draw()
  {
    // Drawing three logic here
  }
}

और अब हमारे पास तीन अलग-अलग कक्षाएं हैं जो अन्य वर्गों के लिए तर्क प्रदान करने के लिए बनाई जा सकती हैं।

public class One : ExampleClassBase
{
  private DrawOne drawOne;

  public One(DrawOne drawOne)
  {
    this.drawOne = drawOne;
  }

  public void Draw()
  {
    this.drawOne.Draw();
  }
}

public class TwoOne : ExampleClassBase
{
  private DrawOne drawOne;
  private DrawTwo drawTwo;

  public One(DrawOne drawOne, DrawTwo drawTwo)
  {
    this.drawOne = drawOne;
    this.drawTwo = drawTwo;
  }

  public void Draw()
  {
    this.drawOne.Draw();
    this.drawTwo.Draw();
  }
}

// the other six classes here

यह दृष्टिकोण कहीं अधिक क्रियात्मक है। लेकिन इसके फायदे हैं।

निम्नलिखित वर्ग पर विचार करें, जिसमें एक बग है:

public class ThreeTwoOne : ExampleClassBase
{
  private DrawOne drawOne;
  private DrawTwo drawTwo;
  private DrawThree drawThree;

  public One(DrawOne drawOne, DrawTwo drawTwo, DrawThree drawThree)
  {
    this.drawOne = drawOne;
    this.drawTwo = drawTwo;
    this.drawThree = drawThree;
  }

  public void Draw()
  {
    this.drawOne.Draw();
    this.drawTwo.Draw();
  }
}

लापता ड्रॉथ्री.ड्रॉ () कॉल को स्पॉट करना कितना सरल है? और यदि आदेश महत्वपूर्ण है, तो ड्रॉ कॉल का क्रम भी देखने और अनुसरण करने में बहुत आसान है।

इस दृष्टिकोण से नुकसान:

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

इस दृष्टिकोण के लाभ:

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

जब भी आपको किसी भी जटिल तर्क नियंत्रण कोड को स्टेटमेंट स्टेटमेंट में लिखे जाने की आवश्यकता महसूस हो तो इस (या समान) दृष्टिकोण पर विचार करें। भविष्य आप खुश होंगे आपने किया।


यह एक बहुत अच्छा उत्तर है और मैं पूरी तरह से दृष्टिकोण से सहमत हूं। मेरे विशेष मामले में इस क्रिया को प्रत्येक के पीछे के तुच्छ कोड को देखते हुए अनंत डिग्री तक ओवरक्लिक किया जाएगा। इसे परिप्रेक्ष्य में रखने के लिए, कल्पना करें कि कोड केवल एक Console.WriteLine (n) का प्रदर्शन कर रहा है। यह व्यावहारिक रूप से समतुल्य है कि मैं जिस कोड के साथ काम कर रहा था वह क्या कर रहा था। अन्य सभी मामलों के 90% में, आपका समाधान निश्चित रूप से OOP का पालन करते समय सबसे अच्छा जवाब है।
PerpetualJ

हाँ उचित बिंदु, यह दृष्टिकोण हर स्थिति में उपयोगी नहीं है। मैं इसे यहाँ रखना चाहता था क्योंकि डेवलपर्स के लिए एक समस्या को हल करने के लिए किसी एक विशेष दृष्टिकोण को ठीक करना सामान्य है और कभी-कभी यह वापस कदम उठाता है और विकल्पों की तलाश करता है।
स्टीफन

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

0

यदि आप यहां एक स्विच का उपयोग करने के इरादे से हैं, तो यदि आप प्रत्येक मामले को अलग से संभालते हैं तो आपका कोड वास्तव में तेज़ होगा

switch(exampleValue)
{
    case One:
        i++;
        break;
    case Two:
        i += 2;
        break;
    case OneAndTwo:
    case Three:
        i+=3;
        break;
    case OneAndThree:
        i+=4;
        break;
    case TwoAndThree:
        i+=5;
        break;
}

प्रत्येक मामले में केवल एक अंकगणितीय ऑपरेशन किया जाता है

जैसा कि दूसरों ने कहा है कि यदि आप गोटो का उपयोग करने पर विचार कर रहे हैं, तो आपको संभवतः अपने एल्गोरिथ्म को पुनर्विचार करना चाहिए (हालांकि मैं यह स्वीकार करूंगा कि सी # केस की कमी हालांकि एक गोटो का उपयोग करने का एक कारण हो सकता है)। एडगर डीजकस्ट्रा का प्रसिद्ध पेपर "जाने माने कथन पर विचार करें हानिकारक"


3
मैं कहूंगा कि यह किसी के लिए एक सरल विचार का सामना करने के लिए एक महान विचार है इसलिए इसे पोस्ट करने के लिए धन्यवाद। मेरे मामले में यह इतना आसान नहीं है।
पेराप्चुअलज

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

कई बूलियन होने पर यह अच्छी तरह से पैमाने पर नहीं होगा।
user949300

0

अपने विशेष उदाहरण के लिए, चूंकि आप वास्तव में गणना से चाहते थे, प्रत्येक चरण के लिए एक do / do-not सूचक था, जो समाधान आपके तीन ifबयानों को फिर से लिखता है switch, वह बेहतर है , और यह अच्छा है कि आपने इसे स्वीकृत उत्तर दिया है ।

लेकिन अगर आप कुछ और अधिक जटिल तर्क यह है कि किया था नहीं तो सफाई से बाहर काम करते हैं, तो मैं अब भी लगता है gotoमें switchबयान भ्रामक। मैं कुछ इस तरह देखना चाहता हूँ:

switch (enumVal) {
    case ThreeOneTwo: DrawThree; DrawTwo; DrawOne; break;
    case ThreeTwo:    DrawThree; DrawTwo; break;
    case ThreeOne:    DrawThree; DrawOne; break;
    case TwoOne:      DrawTwo; DrawOne; break;
    case Two:         DrawTwo; break;
    default:          DrawOne; break;
}

यह सही नहीं है, लेकिन मुझे लगता है कि यह इस तरह से बेहतर है goto। यदि घटनाओं के क्रम इतने लंबे हैं और एक-दूसरे को इतना डुप्लिकेट करते हैं कि यह वास्तव में प्रत्येक मामले के लिए पूर्ण अनुक्रम को बाहर करने के लिए समझ में नहीं आता है, तो मैं gotoकोड के दोहराव को कम करने के बजाय एक सबरूटीन को प्राथमिकता दूंगा ।


0

मुझे यकीन नहीं है कि किसी को वास्तव में इन दिनों गोटो कीवर्ड से नफरत करने का एक कारण है। यह निश्चित रूप से पुरातन है और 99% उपयोग के मामलों में आवश्यक नहीं है, लेकिन यह एक कारण के लिए भाषा की एक विशेषता है।

gotoकीवर्ड से नफरत करने का कारण कोड की तरह है

if (someCondition) {
    goto label;
}

string message = "Hello World!";

label:
Console.WriteLine(message);

ऊप्स! यह स्पष्ट रूप से काम नहीं कर रहा है। messageचर कि कोड रास्ते में परिभाषित नहीं है। इसलिए C # पास नहीं होगा। लेकिन इसका निहितार्थ हो सकता है। विचार करें

object.setMessage("Hello World!");

label:
object.display();

और यह मान लें कि displayफिर इसमें WriteLineकथन है।

इस तरह की बग को खोजना मुश्किल हो सकता है क्योंकि gotoकोड पथ को अस्पष्ट करता है।

यह एक सरलीकृत उदाहरण है। मान लें कि एक वास्तविक उदाहरण लगभग स्पष्ट नहीं होगा। कोड के बीच label:और उपयोग की पचास लाइनें हो सकती हैं message

भाषा इसे सीमित करने में मदद gotoकर सकती है कि इसका उपयोग कैसे किया जा सकता है, केवल ब्लॉकों से बाहर निकलकर। लेकिन C # gotoऐसे ही सीमित नहीं है। यह कोड पर कूद सकता है। इसके अलावा, यदि आप सीमित करने जा रहे हैं, तो gotoयह केवल नाम बदलने के लिए है। अन्य भाषाओं का उपयोग breakब्लॉक से बाहर आने के लिए किया जाता है, या तो एक संख्या के साथ (बाहर निकलने के लिए) या एक लेबल (ब्लॉक में से एक पर)।

की अवधारणा gotoएक निम्न स्तरीय मशीन भाषा निर्देश है। लेकिन हमारे पास उच्च स्तर की भाषाएँ होने का पूरा कारण इतना है कि हम उच्च स्तर के अमूर्त तक सीमित हैं, जैसे कि चर क्षेत्र।

उस सभी ने कहा, यदि आप केस से कूदने के लिए gotoएक switchकथन के अंदर C # का उपयोग करते हैं, तो यह उचित रूप से हानिरहित है। प्रत्येक मामला पहले से ही एक प्रवेश बिंदु है। मुझे अभी भी लगता है कि इसे कॉल करना gotoउस स्थिति में मूर्खतापूर्ण है, क्योंकि यह gotoअधिक खतरनाक रूपों के साथ इस हानिरहित उपयोग का खुलासा करता है। मैं बहुत बल्कि यह चाहूंगा कि वे इसके लिए कुछ का उपयोग करें continue। लेकिन किसी कारण से, वे भाषा लिखने से पहले मुझसे पूछना भूल गए।


2
में C#भाषा आप चर घोषणाओं के ऊपर से नहीं कर सकते हैं। प्रमाण के लिए, एक कंसोल एप्लिकेशन लॉन्च करें और अपने पोस्ट में दिए गए कोड को दर्ज करें। आपको अपने लेबल में एक संकलक त्रुटि मिलेगी जो यह बताती है कि त्रुटि रहित स्थानीय चर 'संदेश' का उपयोग है । ऐसी भाषाओं में जहां इसकी अनुमति है, यह एक मान्य चिंता है, लेकिन सी # भाषा में नहीं।
PerpetualJ

0

जब आपके पास यह कई विकल्प हैं (और जैसा कि आप कहते हैं), तो शायद यह कोड नहीं बल्कि डेटा है।

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


-7

एक लूप का उपयोग करें और ब्रेक और जारी रखने के बीच का अंतर।

do {
  switch (exampleValue) {
    case OneAndTwo: i += 2; break;
    case OneAndThree: i += 3; break;
    case Two: i += 2; continue;
    case TwoAndThree: i += 2;
    // dropthrough
    case Three: i += 3; continue;
    default: break;
  }
  i++;
} while(0);
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.