अशक्त + सत्य कैसे एक स्ट्रिंग है?


112

चूंकि trueएक स्ट्रिंग प्रकार नहीं है, एक स्ट्रिंग कैसे है null + true?

string s = true;  //Cannot implicitly convert type 'bool' to 'string'   
bool b = null + true; //Cannot implicitly convert type 'string' to 'bool'

इसके पीछे का कारण क्या है?


27
आप कुछ ऐसा करते हैं जो समझ में नहीं आता है और फिर संदेश संकलक उत्पन्न नहीं करता है? यह अमान्य C # कोड है ... वास्तव में आप क्या करना चाहते थे?
होगन

19
@ होगन: शुद्ध जिज्ञासा से पूछे गए प्रश्न के लिए कोड यादृच्छिक और पर्याप्त लगता है। बस मेरा अनुमान है ...
BoltClock

8
null + true रिटर्न 1 जावास्क्रिप्ट में
फतह एसीट

जवाबों:


147

विचित्र के रूप में यह लग सकता है, यह बस सी # भाषा कल्पना से नियमों का पालन कर रहा है।

खंड From.३.४ से:

प्रपत्र x op y का संचालन, जहाँ op एक अधिभार बाइनरी ऑपरेटर है, x एक प्रकार X की अभिव्यक्ति है, और y एक प्रकार Y की अभिव्यक्ति है, इस प्रकार संसाधित किया जाता है:

  • ऑपरेशन ऑपरेटर ऑप (x, y) के लिए X और Y द्वारा प्रदान किए गए उम्मीदवार उपयोगकर्ता-परिभाषित ऑपरेटरों का सेट निर्धारित किया जाता है। सेट में एक्स द्वारा प्रदान किए गए उम्मीदवार ऑपरेटरों के संघ और वाई द्वारा प्रदान किए गए उम्मीदवार ऑपरेटर होते हैं, प्रत्येक .37.3.5 के नियमों का उपयोग करके निर्धारित होता है। यदि X और Y एक ही प्रकार हैं, या यदि X और Y एक सामान्य आधार प्रकार से प्राप्त हुए हैं, तो साझा उम्मीदवार ऑपरेटर केवल एक बार संयुक्त रूप में होते हैं।
  • यदि उम्मीदवार उपयोगकर्ता निर्धारित ऑपरेटरों का सेट खाली नहीं है, तो यह ऑपरेशन के लिए उम्मीदवार ऑपरेटरों का सेट बन जाता है। अन्यथा, पूर्वनिर्धारित बाइनरी ऑपरेटर ऑप इम्प्लीमेंटेशन, जिसमें उनके उठाए गए फॉर्म शामिल हैं, ऑपरेशन के लिए उम्मीदवार ऑपरेटरों का सेट बन जाते हैं। किसी दिए गए ऑपरेटर के पूर्वनिर्धारित कार्यान्वयन ऑपरेटर के विवरण में निर्दिष्ट होते हैं ()7.8 §7.12 के माध्यम से)।
  • .37.5.3 के अधिभार संकल्प नियम तर्क सूची (x, y) के संबंध में सर्वश्रेष्ठ ऑपरेटर का चयन करने के लिए उम्मीदवार ऑपरेटरों के सेट पर लागू होते हैं, और यह ऑपरेटर अधिभार संकल्प प्रक्रिया का परिणाम बन जाता है। यदि ओवरलोड रिज़ॉल्यूशन किसी एकल सर्वश्रेष्ठ ऑपरेटर का चयन करने में विफल रहता है, तो बाइंडिंग-टाइम त्रुटि उत्पन्न होती है।

तो, चलिए इसके माध्यम से बारी बारी से चलते हैं।

X यहां अशक्त प्रकार है - या नहीं एक प्रकार है, यदि आप इस तरह से सोचना चाहते हैं। यह कोई उम्मीदवार उपलब्ध नहीं करा रहा है। Y है bool, जो किसी भी उपयोगकर्ता-परिभाषित +ऑपरेटरों को प्रदान नहीं करता है । तो पहला कदम कोई उपयोगकर्ता-परिभाषित ऑपरेटर नहीं पाता है।

संकलक फिर पूर्वनिर्धारित बाइनरी ऑपरेटर + कार्यान्वयन और उनके उठाए गए रूपों को देखते हुए, दूसरे बुलेट बिंदु पर जाता है। ये कल्पना की धारा 7.8.4 में सूचीबद्ध हैं।

यदि आप उन पूर्वनिर्धारित ऑपरेटरों के माध्यम से देखते हैं, तो केवल एक ही लागू होता है string operator +(string x, object y)। तो उम्मीदवार सेट में एक ही प्रविष्टि है। यह अंतिम गोली बिंदु को बहुत सरल बनाता है ... अधिभार संकल्प उस ऑपरेटर को चुनता है, जो एक समग्र अभिव्यक्ति प्रकार देता है string

एक दिलचस्प बात यह है कि यह तब भी होगा जब अन्य उपयोगकर्ता-परिभाषित ऑपरेटर बिना किसी प्रकार के उपलब्ध हैं। उदाहरण के लिए:

// Foo defined Foo operator+(Foo foo, bool b)
Foo f = null;
Foo g = f + true;

यह ठीक है, लेकिन यह एक अशक्त शाब्दिक के लिए उपयोग नहीं किया जाता है, क्योंकि संकलक को देखने के लिए नहीं पता है Foo। यह केवल विचार करना जानता है stringक्योंकि यह पूर्वनिर्धारित ऑपरेटर स्पष्ट रूप से कल्पना में सूचीबद्ध है। (वास्तव में, यह स्ट्रिंग प्रकार द्वारा परिभाषित ऑपरेटर नहीं है ... 1 ) इसका मतलब है कि यह संकलन करने में विफल रहेगा:

// Error: Cannot implicitly convert type 'string' to 'Foo'
Foo f = null + true;

अन्य दूसरे-ऑपरेंड प्रकार कुछ अन्य ऑपरेटरों का उपयोग करेंगे, निश्चित रूप से:

var x = null + 0; // x is Nullable<int>
var y = null + 0L; // y is Nullable<long>
var z = null + DayOfWeek.Sunday; // z is Nullable<DayOfWeek>

1 आप सोच रहे होंगे कि स्ट्रिंग + ऑपरेटर क्यों नहीं है। यह एक उचित सवाल है, और मैं केवल उत्तर पर अनुमान लगा रहा हूं , लेकिन इस अभिव्यक्ति पर विचार करें:

string x = a + b + c + d;

यदि stringसी # संकलक में कोई विशेष आवरण नहीं था, तो यह प्रभावी रूप से समाप्त हो जाएगा:

string tmp0 = (a + b);
string tmp1 = tmp0 + c;
string x = tmp1 + d;

तो यह दो अनावश्यक मध्यवर्ती तार पैदा करता है। हालाँकि, संकलक के भीतर विशेष समर्थन होने के कारण, यह वास्तव में उपरोक्त संकलन करने में सक्षम है:

string x = string.Concat(a, b, c, d);

जो बिल्कुल सही लंबाई का एक ही तार बना सकता है, जिससे एक बार में ही सारा डेटा कॉपी हो जाता है। अच्छा लगा।


'एक समग्र अभिव्यक्ति प्रकार की स्ट्रिंग दे रहा है' मैं बस सहमत नहीं हूं। त्रुटि trueपरिवर्तनीय नहीं होने से आती है string। यदि अभिव्यक्ति मान्य थी तो प्रकार होगा string, लेकिन इस स्थिति में स्ट्रिंग में कनवर्ट करने में विफलता पूरी अभिव्यक्ति को एक त्रुटि बनाती है, और इसलिए कोई प्रकार नहीं है।
लेप्पी

6
@leppie: अभिव्यक्ति है मान्य है, और उसके प्रकार स्ट्रिंग है। "Var x = null + true;" आज़माएँ - यह संकलन और xप्रकार का है string। ध्यान दें कि यहां उपयोग किया गया हस्ताक्षर है string operator+(string, object)- यह (जो ठीक है) में परिवर्तित boolहो रहा objectहै, न कि string
जॉन स्कीट

धन्यवाद जॉन, अब समझते हैं। बीटीडब्ल्यू धारा 14.7.4 कल्पना के संस्करण 2 में।
लेप्पी डे

@leppie: ECMA नंबरिंग में है? यह हमेशा बहुत अलग रहा है :(
जॉन स्कीट

47
20 मिनट में। उन्होंने 20 मिनट में यह पूरी बात लिखी। उसे किताब लिखनी चाहिए या किसी दिन ... ओह इंतजार।
एपीगा

44

इसका कारण यह है कि एक बार जब आप परिचय देते हैं +तो C # ऑपरेटर बाइंडिंग नियम लागू होते हैं। यह +उपलब्ध ऑपरेटरों के सेट पर विचार करेगा और सर्वश्रेष्ठ अधिभार का चयन करेगा। उन ऑपरेटरों में से एक निम्नलिखित है

string operator +(string x, object y)

यह अधिभार अभिव्यक्ति में तर्क प्रकारों के साथ संगत है null + true। इसलिए इसे ऑपरेटर के रूप में चुना जाता है और अनिवार्य रूप से मूल्यांकन किया जाता है ((string)null) + trueजो मूल्य का मूल्यांकन करता है "True"

C # भाषा युक्ति की धारा 7.7.4 में इस रिज़ॉल्यूशन के आसपास का विवरण है।


1
मुझे लगता है कि आईएल उत्पन्न सीधे कॉनैट को कॉल करने के लिए जाता है। मैं सोच रहा था कि मैं वहां के + ऑपरेटर फ़ंक्शन को कॉल करूंगा। कि बस अनुकूलन अनुकूलन होगा?
क्वेंटिन-स्टारिन

1
@qstarin मुझे विश्वास है कि कारण यह है कि वास्तव में एक के operator+लिए नहीं है string। इसके बजाय यह केवल संकलक के दिमाग में मौजूद है और यह बस कॉल के लिए इसे नीचे अनुवादित करता हैstring.Concat
JaredPar

1
@qstarin @JaredPar: मैं अपने उत्तर में थोड़ा और आगे बढ़ गया हूँ।
जॉन स्कीट

11

संकलक एक ऑपरेटर + () के लिए शिकार करता है जो पहले एक अशक्त तर्क ले सकता है। मानक मान प्रकारों में से कोई भी योग्य नहीं है, अशक्त उनके लिए एक वैध मूल्य नहीं है। एक और केवल मैच System.String.operator + () है, इसमें कोई अस्पष्टता नहीं है।

उस ऑपरेटर का दूसरा तर्क भी एक स्ट्रिंग है। यह कपोई जाता है, यह स्पष्ट रूप से बूल को स्ट्रिंग में नहीं बदल सकता है।


10

दिलचस्प है, जो उत्पन्न होता है उसका निरीक्षण करने के लिए परावर्तक का उपयोग करते हुए, निम्न कोड:

string b = null + true;
Console.WriteLine(b);

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

Console.WriteLine(true);

इस "अनुकूलन" के पीछे का तर्क थोड़ा अजीब है जो मुझे कहना चाहिए, और ऑपरेटर चयन के साथ तालमेल नहीं करता है जो मैं उम्मीद करूंगा।

इसके अलावा, निम्नलिखित कोड:

var b = null + true; 
var sb = new StringBuilder(b);

में तब्दील हो गया है

string b = true; 
StringBuilder sb = new StringBuilder(b);

जहां string b = true;वास्तव में संकलक द्वारा स्वीकार नहीं किया जाता है।


8

nullस्ट्रिंग नल के लिए डाली जाएगी, और बूल से स्ट्रिंग के लिए अंतर्निहित कनवर्टर है, इसलिए स्ट्रिंग को trueडाली जाएगी और फिर, +ऑपरेटर को लागू किया जाएगा: यह इस तरह है: स्ट्रिंग str = "+ true.ToString ();

यदि आप इसे Ildasm से जाँचते हैं:

string str = null + true;

यह उतना ही है:

.locals init ([0] string str)
  IL_0000:  nop
  IL_0001:  ldc.i4.1
  IL_0002:  box        [mscorlib]System.Boolean
  IL_0007:  call       string [mscorlib]System.String::Concat(object)
  IL_000c:  stloc.0

5
var b = (null + DateTime.Now); // String
var b = (null + 1);            // System.Nullable<Int32> | same with System.Single, System.Double, System.Decimal, System.TimeSpan etc
var b = (null + new Object()); // String | same with any ref type

पागल?? नहीं, इसके पीछे एक कारण होना चाहिए।

किसी ने फोन किया Eric Lippert...


5

इसका कारण सुविधा है (तारों को जोड़ना एक सामान्य कार्य है)।

जैसा कि बोल्टकॉक ने कहा, '+' ऑपरेटर को संख्यात्मक प्रकारों, तारों पर परिभाषित किया गया है, और हमारे स्वयं के प्रकारों के लिए भी परिभाषित किया जा सकता है (ऑपरेटर ओवरलोडिंग)।

यदि तर्क के प्रकारों पर एक अतिभारित '+' ऑपरेटर नहीं है और वे संख्यात्मक प्रकार नहीं हैं, तो संकलक स्ट्रिंग संघनन में चूक करता है।

कंपाइलर String.Concat(...)'+' का उपयोग करते हुए कॉल को सम्मिलित करता है , और कॉनैट कॉल के कार्यान्वयन को उसमें पारित प्रत्येक ऑब्जेक्ट पर कॉल करना है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.