2009-05-10 8 views
27

मुझे यह जानकर उत्सुकता है कि यह क्यों हो रहा है।सी # कंपाइलर GetType() विधि कॉल के लिए कॉलवर्ट निर्देश उत्सर्जित क्यों कर रहा है?

using System; 

class Program 
{ 
    static void Main() 
    { 
     Object o = new Object(); 
     o.GetType(); 

     // L_0001: newobj instance void [mscorlib]System.Object::.ctor() 
     // L_0006: stloc.0 
     // L_0007: ldloc.0 
     // L_0008: callvirt instance class [mscorlib]System.Type [mscorlib]System.Object::GetType() 

     new Object().GetType(); 

     // L_000e: newobj instance void [mscorlib]System.Object::.ctor() 
     // L_0013: call instance class [mscorlib]System.Type [mscorlib]System.Object::GetType() 
    } 
} 

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

+1

बहुत अच्छा प्रश्न ... मुझे मेरी पुस्तकों के लिए बाहर तक पहुँचने बनाया है। – Gishu

+15

सारांश: केस (1) आभासी विधि का आह्वान करें: कॉलवर्ट उत्पन्न करें। केस (2) निरर्थक रिसीवर पर इंस्टेंस विधि का आह्वान करें: सस्ते नल चेक प्राप्त करने के लिए कॉलवर्ट उत्पन्न करें - हाँ, यह टाइपएफ़ है। केस (3) ज्ञात गैर-शून्य रिसीवर पर इंस्टेंस विधि का आह्वान करें: _avoid_ null check पर कॉल उत्पन्न करें। आपका पहला उदाहरण श्रेणी (2) में आता है, आपका दूसरा उदाहरण श्रेणी (3) में आता है। (कंपाइलर जानता है कि नया कभी वापस नहीं लौटाता है और इसलिए फिर से जांचने की आवश्यकता नहीं है।) –

+0

धन्यवाद एरिक, शून्य जांच बहुत समझ में आता है। आपकी टिप्पणी प्लस गिशू का जवाब चीजों को और स्पष्ट बनाता है! :) –

उत्तर

20

बस सुरक्षित खेलें।

तकनीकी तौर पर सी # संकलक स्थिर तरीकों & तरीकों मूल्य प्रकार पर परिभाषित के लिए नहीं हमेशा उपयोग callvirt

करता है, यह call उपयोग करता है। बहुमत callvirt आईएल निर्देश के माध्यम से प्रदान किया जाता है।

अंतर जो दोनों के बीच वोट घुमाता है वह तथ्य यह है कि call "कॉल करने के लिए उपयोग की जाने वाली वस्तु" शून्य नहीं है। दूसरी तरफ callvirt शून्य के लिए चेक नहीं करता है और यदि आवश्यक हो तो NullReferenceException फेंकता है।

  • स्थैतिक तरीकों के लिए, ऑब्जेक्ट एक प्रकार की वस्तु है और शून्य नहीं हो सकता है। मूल्य प्रकार के लिए Ditto। इसलिए call उनके लिए उपयोग किया जाता है - बेहतर प्रदर्शन।
  • दूसरों के लिए, भाषा डिजाइनरों ने callvirt के साथ जाने का फैसला किया ताकि जेआईटी कंपाइलर सत्यापित करता है कि कॉल करने के लिए उपयोग की जाने वाली वस्तु शून्य नहीं है। गैर वर्चुअल इंस्टेंस विधियों के लिए भी .. उन्होंने प्रदर्शन पर सुरक्षा की सराहना की।

भी देखें: जेफ रिक्टर इस में बेहतर काम करता है - सी # 2 के माध्यम से अपने 'डिजाइनिंग प्रकार' CLR में अध्याय में एड

+0

मुझे लगता है कि वे दोनों डीबगर बंद कॉल के साथ कॉल का उपयोग करते हैं [(पहली बार डीबग में कॉलवर्ट है क्योंकि आप दूसरी पंक्ति पर ब्रेकपॉइंट डाल सकते हैं और ओ के मूल्य को शून्य में बदल सकते हैं)] (http://stackoverflow.com/a/193,955/2,850,543)। –

1

कंपाइलर पहली अभिव्यक्ति में o के वास्तविक प्रकार को नहीं जानता है, लेकिन यह दूसरी अभिव्यक्ति में वास्तविक प्रकार को जानता है। ऐसा लगता है कि यह एक समय में केवल एक बयान देख रहा है।

यह ठीक है, क्योंकि सी # ऑप्टिमाइज़ेशन के लिए जेआईटी पर भारी निर्भर करता है। इस तरह के एक साधारण मामले में यह बहुत संभावना है कि दोनों कॉल रनटाइम पर इंस्टेंस कॉल बन जाएंगी।

मैं नहीं मानता callvirt कभी गैर आभासी तरीकों के लिए उत्सर्जित होता है, लेकिन फिर भी अगर यह था, यह कोई समस्या नहीं है क्योंकि विधि (स्पष्ट कारणों के लिए) कभी नहीं अधिरोहित जा होगी।

+0

जो केवल वर्चुअल विधियों के लिए कॉलवर्ट बताता है। लेकिन GetType वर्चुअल नहीं है। यह सीएलआर के आंतों में कहीं गहराई से लागू एक बाहरी कार्य है (शायद उस क्षेत्र को लौटा रहा है जो ऑब्जेक्ट के vtable या कुछ में संग्रहीत है)। यह हर वस्तु के लिए एक ही विधि है। – Niki

+0

पर्याप्त मेला - मुझे लगता है कि GetType वर्चुअल था। मेरी गलती। मुझे डस्टिन का जवाब पसंद है। – zildjohn01

0

मैं अनुमान लगाता हूं कि ऐसा इसलिए होता है क्योंकि पहले एक चर को असाइन किया जाता है, जिसमें संभावित रूप से GetType ओवरराइड हो सकता है (भले ही हम इसे देख सकें); दूसरा Object के अलावा कुछ भी नहीं हो सकता है।

27

एरिक गुनरसन द्वारा this पुराना ब्लॉग पोस्ट देखें।

क्यों सी # हमेशा callvirt का उपयोग करता है:

यहाँ पोस्ट के पाठ है?

यह प्रश्न आंतरिक सी # उपनाम पर आया, और मैंने सोचा कि उत्तर सामान्य रुचि का होगा। यह माना जा रहा है कि जवाब सही है - यह काफी समय से रहा है।

.NET आईएल भाषा वर्चुअल फ़ंक्शंस को कॉल करने के लिए कॉलवर्ट का उपयोग करने के साथ कॉल और कॉलवर्ट निर्देश दोनों प्रदान करता है। लेकिन यदि आप उस कोड को देखते हैं जो सी # उत्पन्न करता है, तो आप देखेंगे कि यह उन मामलों में "कॉलवर्ट" उत्पन्न करता है जहां कोई वर्चुअल फ़ंक्शन शामिल नहीं है। वह ऐसा क्यों करता है?

मैं अपने पास भाषा डिजाइन नोट्स के माध्यम से वापस गया, और वे स्पष्ट रूप से बताते हैं कि हमने 12/13/1999 को कॉलवर्ट का उपयोग करने का निर्णय लिया था। दुर्भाग्यवश, वे ऐसा करने के लिए हमारे तर्क को नहीं पकड़ते हैं, इसलिए मुझे अपनी याददाश्त से जाना होगा।

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

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

+3

बहुत बुरा कोई तरीका नहीं है कि एक गैर वर्चुअल विधि निर्दिष्ट कर सकती है कि इसे 'कॉलवर्ट' के बिना बुलाया जाना चाहिए क्योंकि यह' string.IsNullOrEmpty' जैसी विधियों को शून्य तारों पर प्रयोग करने योग्य बनाने की अनुमति देने में मददगार होगा। – supercat

+0

@supercat उस तरीके से विस्तार विधियों का उपयोग करना है, भले ही ऑब्जेक्ट शून्य हो, फिर भी यह एक्सटेंशन पहले पैरामीटर में पारित हो जाएगा। – vexe

+0

दुर्भाग्य से आप एक सामान्य विधि पर (प्रभावी रूप से) पर रनटाइम निर्णय बनाने के लिए जेनेरिक फ़ंक्शन में जेनेरिक प्रकारों के साथ विस्तार विधियों का उपयोग नहीं कर सकते हैं। – NetMage

3

के रूप में एक (perhaps-) दिलचस्प एक तरफ ... GetType() है असामान्य है कि virtual नहीं है - इससे very, very odd things की ओर जाता है।

(विकी के रूप में चिह्नित के रूप में यह कुछ हद तक विषय से हटकर वास्तविक सवाल है)

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