2013-01-08 21 views
18

सबसे पहले, पृष्ठभूमि का एक बिट। मेरे प्रश्न के लिए एक विशिष्ट परिदृश्य के लिए प्रश्न पढ़ें और posted here उत्तर दें। मुझे यकीन नहीं है कि अगर अन्य, समान मामले मौजूद हैं लेकिन यह एकमात्र मामला है जिसे मैं जानता हूं।कंपाइलर वैल्यू टाइप रेज़ोल्यूशन और हार्डकोडेड "0" इंटेगर वैल्यू

उपरोक्त "क्विर्क" ऐसा कुछ है जिसे मुझे लंबे समय से पता चला है। मैं हाल ही में कारण की पूरी चौड़ाई को समझ नहीं पाया।

SqlParameter कक्षा पर माइक्रोसॉफ्ट के दस्तावेज स्थिति पर थोड़ा और प्रकाश डालते हैं।

जब आप मूल्य पैरामीटर में एक Object निर्दिष्ट करते हैं, SqlDbType वस्तु के माइक्रोसॉफ्ट .NET फ्रेमवर्क प्रकार से अनुमान लगाया है।

पूर्णांक पैरामीटर मान निर्दिष्ट करने के लिए SqlParameter कन्स्ट्रक्टर के इस अधिभार का उपयोग करते समय सावधानी बरतें। चूंकि यह अधिभार प्रकार Object प्रकार का मान लेता है, तो आपको अभिन्न मान को Object टाइप टाइप करना होगा जब मान शून्य है, जैसा कि निम्नलिखित सी # उदाहरण दर्शाता है।

Parameter = new SqlParameter("@pname", Convert.ToInt32(0));

यदि आप करते हैं इस रूपांतरण प्रदर्शन नहीं, संकलक मानता है कि आप SqlParameter (स्ट्रिंग, SqlDbType) निर्माता अधिभार कॉल करने के लिए कोशिश कर रहे हैं ।

(emph।) जोड़ा

मेरा प्रश्न है क्यों करता है संकलक को लगता है कि जब आप एक कठिन कोडित "0" (और केवल मूल्य "0") है कि आप एक निर्दिष्ट करने के लिए कोशिश कर रहे हैं निर्दिष्ट एक पूर्णांक प्रकार के बजाय, गणना प्रकार? इस मामले में, यह मानता है कि आप SqlDbType मान को 0 के बजाय SqlDbType मान घोषित कर रहे हैं।

यह गैर-सहज ज्ञान युक्त है और, मामलों को और भी खराब बनाने के लिए, त्रुटि असंगत है। मेरे पास पुराने एप्लिकेशन हैं जिन्हें मैंने लिखा है जिन्हें वर्षों से संग्रहित प्रक्रिया कहा जाता है। मैं एप्लिकेशन में बदलाव करूंगा (अक्सर मेरे SQL सर्वर कक्षाओं से भी जुड़ा नहीं होता), एक अद्यतन प्रकाशित करता है, और यह समस्या अचानक एप्लिकेशन को तोड़ देती है।

कंपेलर मूल्य 0 द्वारा उलझन में क्यों है, जब एकाधिक विधि हस्ताक्षर वाले ऑब्जेक्ट में दो समान समान हस्ताक्षर होते हैं जहां एक पैरामीटर एक ऑब्जेक्ट/पूर्णांक होता है और दूसरा एक गणना स्वीकार करता है?

जैसा कि मैंने उल्लेख किया है, मैंने इसे किसी भी अन्य वर्ग पर किसी अन्य निर्माता या विधि के साथ कभी भी समस्या के रूप में नहीं देखा है। क्या यह SqlParameter वर्ग के लिए अद्वितीय है या क्या यह एक बग सी #/नेट के भीतर विरासत में है?

+1

सीधे आपके प्रश्न से संबंधित नहीं है, लेकिन मुझे लगता है कि एक नामांकित पैरामीटर ('नया एसक्यूएल पैरामीटर ("@ pname", value: 0) ''' कनवर्ट.ओआईएनटी 32() 'के बजाय एक बेहतर सलाह होगी। – svick

+0

लोग हमेशा डीबीटीपी और लंबाई निर्दिष्ट क्यों नहीं करते हैं? ऐसा नहीं करना समस्या का पता लगाने और योजना कैश को बर्बाद कर देता है। क्या यह सिर्फ कुछ कीस्ट्रोक को बचाने के लिए है या क्या कोई अन्य लाभ है? – adrianm

+0

@adrianm, ऐसा इसलिए है क्योंकि एक निर्माता मौजूद है जो पैरामीटर नाम और मान लेता है। यदि आप इसका उपयोग नहीं कर सकते हैं तो एसकल्प पैरामीटर वर्ग पर कन्स्ट्रक्टर क्यों है? यह कीस्ट्रोक को सहेजने के बारे में नहीं है, यह दो रचनाकारों के बीच छद्म-टकराव होने पर एक वर्ग पर एक कन्स्ट्रक्टर का उपयोग करने के बारे में है क्योंकि कोई एक वस्तु स्वीकार करता है, जहां दूसरा एक गणना स्वीकार करता है। यदि यह "खराब कोडिंग अभ्यास" है, तो निर्माता जो मूल्य लेता है वह पहले स्थान पर मौजूद नहीं होना चाहिए। – RLH

उत्तर

19

क्योंकि एक शून्य पूर्णांक परोक्ष एक enum के लिए परिवर्तनीय है यह:

enum SqlDbType 
{ 
    Zero = 0, 
    One = 1 
} 

class TestClass 
{ 
    public TestClass(string s, object o) 
    { System.Console.WriteLine("{0} => TestClass(object)", s); } 

    public TestClass(string s, SqlDbType e) 
    { System.Console.WriteLine("{0} => TestClass(Enum SqlDbType)", s); } 
} 

// This is perfectly valid: 
SqlDbType valid = 0; 
// Whilst this is not: 
SqlDbType ohNoYouDont = 1; 

var a1 = new TestClass("0", 0); 
// 0 => TestClass(Enum SqlDbType) 
var a2 = new TestClass("1", 1); 
// => 1 => TestClass(object) 

(Visual C# 2008 Breaking Changes - change 12 से अनुकूलित)

संकलक करता है जब अधिभार संकल्प 0 दोनों SqlDbType और के लिए एक Applicable function member है object रचनाकार क्योंकि:

एक अंतर्निहित रूपांतरण (धारा 6.1) के प्रकार से मौजूद है इसी पैरामीटर

(दोनों SqlDbType x = 0 और object x = 0 मान्य हैं)

SqlDbType पैरामीटर better conversion rules की वजह से object पैरामीटर से बेहतर है के प्रकार के तर्क:

  • हैं T1 और T2 एक ही प्रकार के हैं, न तो रूपांतरण बेहतर है।
    • object और SqlDbType एक ही प्रकार
  • तो ST1 है, C1 बेहतर रूपांतरण है नहीं कर रहे हैं।
    • 0 एक object
  • तो ST2, C2 बेहतर रूपांतरण है नहीं है।
    • 0 एक SqlDbType
  • T1 से T2 को एक अंतर्निहित रूपांतरण मौजूद है, तो नहीं है, और T2 से T1 करने के लिए कोई अंतर्निहित रूपांतरण मौजूद है, C1 बेहतर रूपांतरण है।
    • SqlDbType को object से कोई अंतर्निहित रूपांतरण मौजूद
  • T1 को T2 से एक अंतर्निहित रूपांतरण मौजूद है, और T1 से T2 करने के लिए कोई अंतर्निहित रूपांतरण मौजूद है, C2 बेहतर रूपांतरण है।
    • object करने के लिए SqlDbType से एक अंतर्निहित रूपांतरण मौजूद है, इसलिए SqlDbType बेहतर रूपांतरण

ध्यान दें कि बिल्कुल का गठन एक निरंतर 0 है (काफी आसानी से) Visual C# 2008 (माइक्रोसॉफ्ट के कार्यान्वयन में बदल है सी # spec के रूप में) @Eric अपने जवाब में बताते हैं।

+0

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

+0

क्या यह हो सकता है कि आपने पिछली बार संकलित किया था, आपने 'विज़ुअल सी # 2005' का उपयोग किया था और इस बार आपने '2008' का उपयोग किया था और आप उनके द्वारा किए गए ब्रेकिंग परिवर्तनों से काट चुके हैं? अन्यथा बिट सड़ांध के मानक मामले की तरह लगता है। – RichardTowers

+0

मैंने अपना प्रश्न हटा दिया, क्योंकि मुझे आश्चर्य है कि यह समस्या का कोई हिस्सा रहा है या नहीं। पिछली बार, मैंने संकलित किया था कि यह एप्लिकेशन 2011 में था, इसलिए मैं वीएस 2010 का उपयोग कर रहा था। मैं हमेशा अपनी परियोजनाओं को नवीनतम वीएस संस्करण में अपडेट करता हूं, लेकिन नेट रनटाइम संस्करण नहीं, जब वीएस का एक नया संस्करण आता है। मुझे नहीं पता, लेकिन मुझे यह स्वीकार करना होगा कि यह एक पूर्ण रहस्य है। – RLH

1

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

+0

लिपर्ट के ब्लॉग के संदर्भ को पोस्ट करने के लिए धन्यवाद। यह देखना अच्छा लगता है कि कुछ दुःख इस व्यवहार को लागू करने का निर्णय लेने में चला गया। जैसा कि हमेशा मामला लगता है, जटिल समस्याएं अक्सर जटिल परिस्थितियों से उत्पन्न होती हैं। – RLH

0

पर अधिभारित किया, अधिभारित विधि के प्रकार को हल करते समय, सी # सबसे विशिष्ट विकल्प का चयन करता है। SqlParameter कक्षा में दो रचनाकार हैं जो वास्तव में दो तर्क लेते हैं, SqlParameter(String, SqlDbType) और SqlParameter(String, Object)।जब आप शाब्दिक 0 प्रदान करते हैं, तो इसे किसी ऑब्जेक्ट के रूप में या SqlDbType के रूप में व्याख्या किया जा सकता है। चूंकि SqlDbType ऑब्जेक्ट से अधिक विशिष्ट है, इसलिए यह इरादा माना जाता है।

आप this answer में ओवरलोड रिज़ॉल्यूशन के बारे में अधिक पढ़ सकते हैं।

1

यह इस तथ्य के कारण होता है कि पूर्णांक अक्षर 0 किसी भी enum प्रकार के लिए एक अंतर्निहित रूपांतरण है। सी # विनिर्देश राज्यों:

6.1.3 अंतर्निहित गणना रूपांतरण

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

परिणामस्वरूप, इस मामले में सबसे विशिष्ट अधिभार SqlParameter(string, DbType) है।

यह अन्य int मानों के लिए लागू नहीं होता है, इसलिए SqlParameter(string, object) कन्स्ट्रक्टर सबसे विशिष्ट है।

12

रिचर्ड टावर्स का जवाब उत्कृष्ट है, लेकिन मैंने सोचा कि मैं इसमें थोड़ा सा जोड़ूंगा।

जैसा कि अन्य उत्तरों ने इंगित किया है, व्यवहार का कारण है (1) शून्य किसी भी enum में परिवर्तनीय है, और स्पष्ट रूप से ऑब्जेक्ट करने के लिए, और (2) किसी भी enum प्रकार अधिक विशिष्ट उस ऑब्जेक्ट है, इसलिए विधि जो एक enum लेता है इसलिए अधिभार संकल्प द्वारा बेहतर विधि के रूप में चुना जाता है। प्वाइंट दो मुझे आत्म-स्पष्टीकरण की उम्मीद है, लेकिन क्या एक बिंदु बताता है?

सबसे पहले, यहां विनिर्देश से दुर्भाग्यपूर्ण विचलन है। विनिर्देश कहता है कि शाब्दिक शून्य, यानी 0 वास्तव में शब्द कोड में दिखाई देने वाले को किसी भी enum प्रकार में परिवर्तित किया जा सकता है। संकलक वास्तव में लागू करता है कि निरंतर शून्य इस प्रकार परिवर्तित हो सकता है। इसका कारण एक बग के कारण है जिससे संकलक कभी-कभी निरंतर शून्य और कभी-कभी अजीब और असंगत तरीके से अनुमति नहीं देता है। समस्या को हल करने का सबसे आसान तरीका निरंतर शून्य की अनुमति देना था। आप विस्तार यहाँ में इस बारे में पढ़ सकते हैं:

http://blogs.msdn.com/b/ericlippert/archive/2006/03/28/the-root-of-all-evil-part-one.aspx

दूसरा, शून्य किसी भी enum कन्वर्ट करने के लिए अनुमति देने के लिए कारण सुनिश्चित करने के लिए कि यह हमेशा एक "झंडे" enum को शून्य करने के लिए संभव है। अच्छी प्रोग्रामिंग अभ्यास यह है कि प्रत्येक "झंडे" enum का मूल्य "कोई नहीं" होता है जो शून्य के बराबर होता है, लेकिन यह एक दिशानिर्देश है, आवश्यकता नहीं है। सी # 1.0 के डिजाइनरों ने सोचा कि यह अजीब लग रहा है कि आपको

for (MyFlags f = (MyFlags)0; ... 

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

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

+0

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

+0

इसे समझाने के लिए धन्यवाद। मैं * enums के लिए पूर्ण रूप से परिवर्तनीय 0 बनाने के पीछे तर्क के बारे में सोच रहा था। – RichardTowers

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