2012-04-18 11 views
6

आईएल हमेशा इस तरह के मामले में virtual तरीकों के लिए callvirt अनुदेश का उपयोग नहीं करता:कॉलवर्ट आईएल निर्देश वर्चुअल तरीकों में पुनरावर्ती आमंत्रण क्यों करता है?

class MakeMeASandwich{ 
    public override string ToString(){ 
    return base.ToString(); 
    } 
} 

इस मामले में, यह कहा जाता है कि आईएल variable है कि क्या जांच करने के लिए callvirt जहां callvirt उत्पादन किया जाता है के बजाय call का उत्पादन करेगा null या नहीं और NullReferenceException अन्यथा फेंकता है।

  1. क्यों करता है, तो callvirtcall के बजाय प्रयोग किया जाता है एक पुनरावर्ती मंगलाचरण ढेर अतिप्रवाह तक होता है?
  2. यदि call का उपयोग किया जाता है, तो यह जांच कब करता है कि यह विधियों को कॉल करने के लिए उपयोग किए जाने वाले आवृत्ति परिवर्तक शून्य है या नहीं?
+0

@ लुसेरो: नहीं, मैं इसके बारे में एक किताब पढ़ रहा हूं और जब मुझे पुस्तक में कोई जवाब नहीं मिला तो यह मेरे दिमाग में आया। – Tarik

+0

मेरे पास (मुझे लगता है) मैंने स्पष्ट किया है कि मैं आपका प्रश्न '2' मानता हूं। अगर मैं गलत हूं, तो बस मेरे संशोधन को रोलबैक करें :) – AakashM

+0

@AakashM: वास्तव में यह वही था जो मैं पूछ रहा था, ऐसा लगता है कि 'कॉलवर्ट' यह जांच नहीं करता है कि चर खाली है या नहीं :) – Tarik

उत्तर

10

क्यों करता है, तो callvirt कॉल के बजाय प्रयोग किया जाता है एक पुनरावर्ती मंगलाचरण ढेर अतिप्रवाह तक होता है?

क्योंकि फिर अपना कोड बिल्कुल के रूप में एक ही है:

override string ToString() 
{ 
    return this.ToString(); 
} 

जो स्पष्ट रूप से एक अनंत प्रत्यावर्तन है, बशर्ते कि विधि दिया ToString के सबसे-अधिभावी संस्करण है।

यदि कॉल का उपयोग किया जाता है, तो यह कैसे जांचता है कि यह विधियों को कॉल करने के लिए उपयोग किए जाने वाले आवृत्ति परिवर्तक शून्य है या नहीं?

प्रश्न उत्तरदायी नहीं है क्योंकि सवाल झूठ बोलता है।कॉल निर्देश नहीं यह देखने के लिए जांचें कि रिसीवर का संदर्भ शून्य है या नहीं, इसलिए पूछें कि कॉल निर्देश निर्देश शून्य के लिए क्यों कोई समझ नहीं आता है।

मुझे अलग तरीके से व्यक्त है कि कुछ बेहतर सवाल में आप के लिए करते हैं:

क्या परिस्थितियों में सी # संकलक एक फोन एक callvirt बनाम उत्पन्न करता है?

सी # कोड एक आभासी विधि पर एक गैर आभासी कॉल कर रही है तो संकलक एक फोन है, न कि callvirt उत्पन्न करनी चाहिये। वर्चुअल विधि को कॉल करने के लिए base का उपयोग करते समय यह वास्तव में होता है।

सी # कोड एक आभासी कॉल कर रही है तो संकलक एक callvirt उत्पन्न करनी चाहिये।

सी # कोड एक गैर आभासी विधि पर एक गैर आभासी कॉल कर रही है तो संकलक उत्पन्न या तो फोन या callvirt चुन सकते हैं। या तो काम करेगा। सी # कंपाइलर आम तौर पर एक कॉलवर्ट उत्पन्न करने का विकल्प चुनता है।

कॉल निर्देश स्वचालित रूप से शून्य जांच नहीं करता है, लेकिन कॉलवर्ट करता है। यदि सी # कंपाइलर कॉलवर्ट की बजाय कॉल उत्पन्न करने का विकल्प चुनता है, तो क्या यह एक शून्य जांच उत्पन्न करने के लिए भी बाध्य है?

नहीं। सी # कंपाइलर शून्य जांच को छोड़ सकता है अगर रिसीवर पहले से ही शून्य नहीं है। उदाहरण के लिए, यदि आपने गैर-वर्चुअल विधि एम के लिए (new C()).M() कहा है तो संकलक के लिए call निर्देश उत्पन्न करने के लिए यह कानूनी होगा। हम जानते हैं कि (1) विधि वर्चुअल नहीं है, इसलिए इसे callvirt होना आवश्यक नहीं है; हम चुन सकते हैं कि callvirt का उपयोग करना है या नहीं। और हम जानते हैं (2) कि new C() कभी शून्य नहीं है, इसलिए हमें शून्य जांच उत्पन्न करने की आवश्यकता नहीं है।

यदि सी # कंपाइलर नहीं जानता है कि रिसीवर शून्य नहीं है, तो यह या तो कॉलवर्ट उत्पन्न करेगा, या यह कॉल के बाद एक शून्य जांच उत्पन्न करेगा।

+0

तो आप कहते हैं "चूंकि 'कॉलवर्ट' वास्तविक प्रकार को कॉल कर रहा है जो पहले वर्चुअल विधि को लागू करता है और बिंदु पर' आधार 'बनता है, यह स्वयं रिकर्सव्लो' का कारण बनता है? क्या मैं सही हूँ? – Tarik

+0

@ ब्रेवार्ड: कॉलवर्ट दो चीजें करता है। (1) यह जांचता है कि रिसीवर शून्य है या नहीं। (2) यदि रिसीवर शून्य नहीं है तो यह रिसीवर की * आभासी फ़ंक्शन तालिका * में विधि को देखता है, जो रनटाइम को बताता है * इस विधि का संस्करण क्या है जो सबसे व्युत्पन्न प्रकार * पर टाइप किया गया है वस्तु। फिर वह उस विधि को बुलाता है। –

+0

@ ब्रेवार्ड: मुझे लगता है कि लेखों की मेरी श्रृंखला बताती है कि वर्चुअल विधियों का काम आपके लिए कैसे हो सकता है। http://blogs.msdn.com/b/ericlippert/archive/2011/03/17/implementing-the-virtual-method-pattern-in-c-part-one.aspx –

3
  1. callvirt MakeMeASandwich कार्यान्वयन, नहीं वस्तु कार्यान्वयन कॉल करेंगे। इस तरह आप अपना ढेर ओवरफ्लो प्राप्त करते हैं।

  2. प्रारंभिक कॉल कॉलवर्ट के साथ था, जो स्थापित करता है कि संदर्भ शून्य नहीं है। अगर नियंत्रण इस ToString कार्यान्वयन के अंदर है, तो आप पहले से ही जानते हैं कि एक वस्तु है।

+0

क्या आप आइटम को थोड़ा और अधिक समझा सकते हैं? 'कॉलवर्ट' 'मेकमेसैंडविच' को कॉल करेगा, इसलिए यह 'बेस' कहलाता है, यहां एक स्टैक ओवरफ़्लो क्यों है? – Tarik

+0

2 शायद बिल्कुल सही नहीं है - 'यह' शून्य हो सकता है (क्योंकि मेकमेसैंडविच। टोस्टिंग को 'कॉल' किया जा सकता था) और कोई अतिरिक्त चेक नहीं किया जाएगा - संबंधित आईएल बनाकर जांचना उचित रूप से आसान होना चाहिए। –

+1

@ ब्रेवार्ड, बस आईएल लिखें और हाथ से ट्रेस करें - आप देखेंगे कि अगर बेस। टॉस्ट्रिंग का अनुवाद 'कॉलवर्ट बेस' टूस्टिंग 'में किया गया है तो यह वास्तव में कॉल मेकमेसैंडविच। टॉस्ट्रिंग' के बराबर है क्योंकि यह ToString के लिए नवीनतम ओवरराइड का उपयोग करता है। –

2

कॉलवर्ट सबसे व्युत्पन्न विधि उपलब्ध है। इस मामले में यह MakeMeASandwich.ToString() है।

कॉलवर्ट का उद्देश्य नल की जांच करने के लिए नहीं बल्कि वर्चुअल विधि कॉल करने के लिए भी है।

3
  1. call क्योंकि यह वहीं जो विधि कॉल करने के लिए जाना जाता है प्रयोग किया जाता है, और यह क्रम को देखा नहीं किया जाना चाहिए (callvirt विधि सबसे विशिष्ट वर्ग है, जो तब कारणों में परिभाषित कॉल करने के लिए कोड का कारण होगा आपका ढेर ओवरफ्लो)।

  2. callvirt एक शून्य जांच का तात्पर्य है, जबकि call नहीं है।

2

कॉल के बजाय कॉलवर्ट का उपयोग होने पर स्टैक ओवरफ़्लो तक एक पुनरावर्ती आमंत्रण क्यों होता है?

जैसा कि अन्य ने उत्तर दिया है, callvirt विधि को लगभग कॉल करता है। यह के रूप में यदि आप लिखा था

public override string ToString() { 
    return ToString(); 
} 

कॉल प्रयोग किया जाता है, तो यह कैसे जांच करता है कि उदाहरण चर यह तरीकों कॉल करने के लिए उपयोग करता रिक्त है या नहीं आते है?

अन्य ने उल्लेख किया है कि आप पहले ही जानते हैं कि this शून्य नहीं है। यह गलत है। यदिcallvirt का उपयोग MakeMeASandwich.ToString पर कॉल करने के लिए किया जाता है, तो हाँ, this शून्य नहीं हो सकता है।हालांकि, कोई आवश्यकता नहीं है कि callvirt आपके फ़ंक्शन को कॉल करने के लिए उपयोग किया जाता है। अन्य भाषाएं आपको इस तरह से कॉल लिखने की अनुमति देती हैं कि call ऑपोड उत्पन्न होता है, और उस स्थिति में, कोई शून्य जांच नहीं की जाती है। मुझे लगता है कि सी ++/सीएलआई इसे makeMeASandwich->MakeMeASandwich::ToString() के साथ अनुमति देता है, लेकिन मुझे पूरी तरह से यकीन नहीं है। आप यह सुनिश्चित करने के लिए नहीं जान सकते कि this शून्य नहीं है जब तक कि आप जांच न करें।

+0

क्या आप कृपया "विधि को बुलाए जाने के बारे में बता सकते हैं? मुझे पता है कि 'आभासी' का अर्थ है कि आप इसे व्युत्पन्न कक्षा में ओवरराइड कर सकते हैं। – Tarik

+2

@ ब्रेवार्ड: विधि को कॉल करने का अर्थ है विधि की विधि से विधि का पता प्राप्त करना आभासी तरीकों। 'System.Object', उदाहरण के लिए, वर्चुअल विधि तालिका में (दूसरों के बीच) एक' ToString() 'विधि में स्लॉट परिभाषित करता है। विधि * जो स्लॉट में जाती है * को ओवरराइडिंग कक्षाओं द्वारा परिभाषित किया जा सकता है। एक ओवरराइडिंग क्लास स्लॉट के लिए एक विधि को परिभाषित नहीं करती है, फिर विरासत श्रृंखला में सबसे अधिक व्युत्पन्न वर्ग द्वारा परिभाषित विधि का उपयोग किया जाता है। – phoog

+1

@ ब्रेवार्ड एक वर्ग उदाहरण {सार्वजनिक वर्चुअल शून्य एफ के साथ एक ठोस उदाहरण का उपयोग करने के लिए ();} ', 'कक्षा बी: ए {सार्वजनिक ओवरराइड शून्य एफ();}',' ए ए = नया बी(); ', वस्तुतः कॉलिंग (' कॉलवर्ट') 'ए :: एफ()' वास्तव में समाप्त होता है 'बी :: एफ() 'को कॉल करते समय, सीधे कॉलिंग (' कॉलवर्ट') 'ए :: एफ()' का अर्थ है कि आप 'ए' द्वारा प्रदान किए गए कार्यान्वयन को कॉल करते हैं । यह मुख्य प्रश्न rephrased बहुत सुंदर है :) – hvd

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