मैं गणना मानों को 0.0 क्यों निर्दिष्ट कर सकता हूं, लेकिन 1.0 को नहीं


90

जिज्ञासा से बाहर: मैं 0.0 को एक चर के लिए असाइन कर सकता हूं जो कि एक गणन प्रकार का है, लेकिन 1.0 नहीं? निम्नलिखित कोड पर एक नजर:

public enum Foo
{
    Bar,
    Baz
}

class Program
{
    static void Main()
    {
        Foo value1 = 0.0;
        Foo value2 = 1.0;   // This line does not compile
        Foo value3 = 4.2;   // This line does not compile
    }
}

मैंने सोचा था कि संख्यात्मक प्रकार और गणना मूल्यों के बीच रूपांतरण केवल जातियों के माध्यम से अनुमति है? यही कारण है कि मैं फू लिख सकता हूं value2 = (Foo) 1.0;ताकि पंक्ति 2 में Mainसंकलन हो सके। 0.0C # में मान के लिए अपवाद क्यों है ?


17
मेरे लिए यह अजीब है, कि आप कस्टम एनुम को डबल शाब्दिक 0.0 असाइन कर सकते हैं। ऐसा नहीं है कि आप कस्टम enum को शाब्दिक निर्दिष्ट नहीं कर सकते1.0 ।
— इल्या इवानोव

2
मुझे संदेह है कि संकलक 0इसके बजाय इसका इलाज कर रहा है । मैं एक ऐसी ही सवाल एक बार और Rawling एक महान उत्तर पोस्ट किया था यहां ।
— एमिकेबल

2
IdeOne इसका संकलन नहीं करता है।
— जॉनी मोप

जवाबों:


98

यह एक बग है जिसे आप 0.0 का उपयोग कर सकते हैं। संकलक केवल 0 के रूप में शून्य के मूल्य के साथ सभी निरंतर अभिव्यक्तियों को स्पष्ट करता है।

अब, कंपाइलर के लिए यह सही है कि intवह C # 5 विनिर्देशन के खंड ६.१.३ के अनुसार ० की अपनी निरंतर अभिव्यक्ति से एक अंतर्निहित रूपांतरण की अनुमति दे :

एक अंतर्निहित गणना रूपांतरण दशमलव-पूर्णांक-शाब्दिक 0 को किसी भी एनुम-प्रकार और किसी भी अशक्त-प्रकार में परिवर्तित करने की अनुमति देता है जिसका अंतर्निहित प्रकार एक एनुम-प्रकार है। बाद के मामले में रूपांतरण का मूल्यांकन अंतर्निहित एनम-प्रकार में परिवर्तित करके और परिणाम को लपेटकर ()4.1.10) किया जाता है।

मैंने पहले इस बारे में C # टीम के साथ बात की है: मानों को मानने के लिए उन्होंने 0.0 (और वास्तव में 0.0m और 0.0f) से आकस्मिक रूपांतरण को हटाना पसंद किया होगा , लेकिन दुर्भाग्य से मैं इसे इकट्ठा करता हूं बहुत अधिक कोड - भले ही इसे कभी भी पहली जगह की अनुमति नहीं दी जानी चाहिए।

मोनो mcsसंकलक इन सभी फ़्लोटिंग पॉइंट रूपांतरणों को प्रतिबंधित करता है , हालांकि यह अनुमति देता है:

const int Zero = 0;
...

SomeEnum x = Zero;

तथ्य यह है कि बावजूद Zeroएक निरंतर अभिव्यक्ति लेकिन है नहीं एक दशमलव-पूर्णांक शाब्दिक।

मैं भविष्य में किसी भी पूर्णांक स्थिर अभिव्यक्ति की अनुमति देने के लिए 0 (यानी की नकल mcs) के साथ सी # विनिर्देश परिवर्तन को देखकर आश्चर्यचकित नहीं होगा , लेकिन मैं अस्थायी बिंदु रूपांतरणों की कभी आधिकारिक तौर पर सही होने की उम्मीद नहीं करूंगा । (मैं सी # के भविष्य की भविष्यवाणी करने से पहले गलत था, बिल्कुल ...)


3
कल्पना के अनुसार, यह केवल शाब्दिक होने के लिए है 0. इसलिए इसे अस्वीकार करना चाहिए 1-1- के intमूल्य के साथ एक स्थिर अभिव्यक्ति 0। लेकिन जैसा कि आप देखते हैं, संकलक यहां कल्पना के अनुरूप है।
— डेमियन_इन् यूबेलिवर

4
it broke too much code- ऐसे कोड लिखने के लिए किसी भी कारण की कल्पना करना वास्तव में कठिन है।
— इल्या इवानोव

1
@ObsidianPhoenix: मुझे यकीन नहीं है कि आपका क्या मतलब है। यह इसके बराबर है SomeEnum x = (SomeEnum) 0;:। यही कारण है कि नामांकित शून्य मान है या नहीं।
— जॉन स्कीट

2
@ObsidianPhoenix: ठीक है, क्योंकि Test.Foo1 का मान 0 नहीं है ... फिर से, यह बिल्कुल वैसा ही है जैसे कि आपने लिखा है Test v1 = (Test) 0;- और यह व्यवहार किसी भी मूल्य के लिए है जो कि एनम में एक नामित मान नहीं है।
— जॉन स्कीट

2
@JonSkeet यह रोजलिन में तय होने जा रहा है?
— अधिकतम

98

जॉन का जवाब सही है। मैं इसमें निम्नलिखित बिंदु जोड़ूंगा।

  • मैं इस मूर्खतापूर्ण और शर्मनाक बग का कारण बना। क्षमा याचनाएं।

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

  • जिस व्यवहार को मैं लागू करने का प्रयास कर रहा था, वह वास्तव में थोड़ा अलग बग के लिए एक समाधान था। आप पूरी भयानक कहानी यहां पढ़ सकते हैं: https://docs.microsoft.com/en-us/archive/blogs/ericlippert/the-root-of-all-evil-part-one और https://docs.microsoft .com / en-us / संग्रहीत / ब्लॉग / ericlippert / जड़-सभी-बुराई-भाग-दो (पाठ: पुराने को ठीक करते समय नए बदतर बगों को पेश करना बहुत आसान है।)

  • सी # टीम ने इसे ठीक करने के बजाय इस छोटी गाड़ी के व्यवहार को सुनिश्चित करने का फैसला किया क्योंकि बिना किसी सम्मोहक लाभ के मौजूदा कोड को तोड़ने का जोखिम बहुत अधिक था। (पाठ: यह पहली बार सही है!)

  • इस व्यवहार को संरक्षित करने के लिए मैंने रोसेलिन में जो कोड लिखा था IsConstantNumericZero, उसे https://github.com/dotnet/roslyn/blob/master/src/Compilers/CSharp/Portable/Binder/Sememics/Conversions/ConversionsBase.cs . विधि में पाया जा सकता है। - यह अधिक विवरण के लिए देखें कि वास्तव में रोजलिन व्यवहार क्या है। मैंने रूपांतरण निर्देशिका में लगभग सभी कोड लिखे; मैं आपको यह सब पढ़ने के लिए प्रोत्साहित करता हूं क्योंकि टिप्पणियों में विनिर्देशन से C # प्राप्त करने के बारे में कई दिलचस्प तथ्य हैं। मैं उन्हें खोजने के लिए आसान बनाने के लिए कल्पना के साथ प्रत्येक को सजाया।

ब्याज की एक और बात: C # किसी भी एनुम इनिशियलाइज़र को उसके शून्य की परवाह किए बिना उपयोग करने की अनुमति देता है :

enum E { A = 1 }
enum F { B = E.A }  // ???

कल्पना कुछ अस्पष्ट है कि क्या यह कानूनी होना चाहिए या नहीं, लेकिन फिर से, क्योंकि यह लंबे समय तक संकलक में रहा है, नए संकलक के व्यवहार को बनाए रखने की संभावना है।


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

The C# team decided to enshrine this buggy behaviour rather than fixing it because the risk of breaking existing code for no compelling benefit was too high.मुझे नहीं लगता कि ऐसे कई लोग होंगे जो इस व्यवहार पर भरोसा करते हैं, और यह इन विषमताओं में से एक है जिसे ठीक करने के लिए बेहतर हो सकता है। हालांकि, यह वास्तव में या तो बहुत नुकसान नहीं करता है (कल्पना को लागू करने वाली परियोजनाओं को छोड़कर)।
— एदियाकापी

5
@ एदियाकपी: वास्तव में, प्रभावित लोगों की संख्या छोटी होनी चाहिए; यह शून्य नहीं है। C # टीम ब्रेकिंग परिवर्तनों को बहुत गंभीरता से लेती है। आपके लिए यह कहना आसान है कि इसे ठीक करना बेहतर है; आपको अपने ग्राहकों को शिकायत करने वाले अनियमित ग्राहकों से निपटने की ज़रूरत नहीं है, जो यह कहते हैं कि आपके तुच्छ परिवर्तन से कोई लाभ नहीं हुआ है, जो एक दिन में उनके सिस्टम एकीकरण में देरी करता है।
— एरिक लिपर्ट

3
ये और ख़राब हो जाता है। ऐसे सभी ब्रेकिंग परिवर्तन (आदर्श रूप से) Microsoft के फ्रेमवर्क माइग्रेशन गाइड पर सूचीबद्ध होंगे। यह सूची जितनी लंबी होगी, उतने ही संकोच करने वाले उपयोगकर्ता अपने आवेदन को स्थानांतरित करने के लिए होंगे। इसलिए, यहां तक ​​कि एक मामूली ब्रेकिंग परिवर्तन का कारण बनता है: 1. अनुप्रयोगों की एक छोटी संख्या को तोड़ने के लिए। 2. उपयोगकर्ताओं की एक छोटी संख्या को अपग्रेड करने से मना करना (भले ही समस्या उन्हें प्रभावित न करे)। 3. उपयोगकर्ताओं की एक छोटी संख्या संसाधनों का मूल्यांकन करने के लिए यदि ब्रेकिंग परिवर्तन उन्हें प्रभावित करता है। 4. सभी से शिकायत करने के लिए # 1, # 2, और # 3 के उपयोगकर्ता।
— ब्रायन

@EricLippert "अगर कल्पना कुछ अस्पष्ट है", तो क्या यह कल्पना को अद्यतन करने के लिए समझ में नहीं आएगा? (वास्तविक प्रश्न!)
— जेम्स

10

C # में एनुमिनेशन परिभाषा अभिन्न मूल्यों द्वारा हैं। संगति के लिए C # को इनमें से किसी भी कार्य को स्वीकार नहीं करना चाहिए, लेकिन 0.0चुपचाप इसे अभिन्न माना जाता है 0। यह संभवतः सी से एक होल्डओवर है, जहां शाब्दिक 0रूप से विशेष रूप से व्यवहार किया गया था और अनिवार्य रूप से किसी भी प्रकार - पूर्णांक, फ्लोटिंग पॉइंट नंबर, अशक्त सूचक… आप इसे नाम दे सकते हैं।


3
सवाल यह है कि क्यों ? यदि आप जाते हैं IL- यह स्टैक पर पूर्णांक मान पर जोर दे रहा हैIL_0001: ldc.i4.0
— इल्या इवानोव

@IlyaIvanov अपडेट देखें। लेकिन ईमानदार होने के लिए, जवाब "कोई अच्छा कारण नहीं है"।
— कोनराड रुडोल्फ

2
मुझे लगता है कि यह उन मामलों में से एक है जहां अगर आप सी # कल्पना को देखते हैं , तो यह कानूनी नहीं है, लेकिन यदि आप किसी ऐसे सी # कंपाइलर को देखते हैं जो एमएस ने उत्पादित किया है, तो यह ऐसा करता है।
— डेमियन_इन_अनबेलियर

3

enum का वास्तव में इरादा है (सभी भाषाओं में जो इसका समर्थन करते हैं) एक संख्यात्मक मूल्यों के बजाय सार्थक और अद्वितीय तार (लेबल) के साथ काम करने का एक तरीका है। इस प्रकार, आपके उदाहरण में, आपको केवल फू एन्यूमरेटेड डेटा प्रकार के साथ काम करते समय बार और बाज का उपयोग करना चाहिए । आपको एक पूर्णांक का उपयोग (तुलना, या असाइन करना) कभी नहीं करना चाहिए, भले ही कई संकलक आपको इसके साथ दूर कर देंगे ( आमतौर पर एम्स हैं) आंतरिक रूप से पूर्णांक ), और इस मामले में, एक 0.0 को लापरवाही से संकलक द्वारा 0 के रूप में माना जाता है।

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


मुझे पता है कि मैं शगुन का उपयोग सी # में उस तरह से नहीं करता हूं - लेकिन मुझे यह मस्तिष्क चिढ़ाने वाला लगा और मैं जानना चाहता था कि 0.0 काम क्यों कर रहा है, लेकिन 1.0 नहीं है। मुझे पता था कि इसे C # कंपाइलर के साथ कुछ होना चाहिए क्योंकि आप देख सकते हैं कि IL कोड इसके लिए Foo v1 = 0.0;भी समान है Foo v2 = Foo.Bar।
— feO2x
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.