2010-11-27 15 views
5

मैं समझने की कोशिश कर रहा हूं कि सीएलआर संदर्भ प्रकार और बहुरूपता को कैसे लागू करता है। मैंने डॉन बॉक्स के अनिवार्य .Net Vol 1 को संदर्भित किया है जो अधिकांश सामानों को कैलिफ़ाई करने में बहुत मददगार है। लेकिन जब मैं बेहतर समझने के लिए कुछ आईएल कोड के साथ खेलने की कोशिश करता हूं तो मैं निम्नलिखित मुद्दे से अटक गया हूं।कॉलवर्ट हुड के नीचे कैसे काम करता है?

मैं जितनी जल्दी हो सके समस्या को समझाने की कोशिश करूंगा। निम्नलिखित कोड पर विचार करें

class Base 
{ 
    public void m() 
    { 
     Console.WriteLine("Base.m"); 
    } 
} 
class Derived : Base 
{ 
    public void m() 
    { 
     Console.WriteLine("Derived.m"); 
    } 
} 

अब मुख्य विधि नीचे दिखाया गया है की आईएल के साथ एक सरल सांत्वना आवेदन पर विचार करें। मैं आईएल मैन्युअल रूप से समझने के लिए संकलक द्वारा बनाई गई और ILAsm.exe

के साथ फिर से इकट्ठे
.class private auto ansi beforefieldinit Console1.Program 
     extends [mscorlib]System.Object 
{ 
    .method private hidebysig static void Main(string[] args) cil managed 
    { 
     .entrypoint 
     // Code size  44 (0x2c) 
     .maxstack 1 
     .locals init ([0] class Console1.Base d) 
     nop 
     newobj  instance void Console1.Base::.ctor() 
     stloc.0 
     ldloc.0 
     callvirt instance void Console1.Derived::m() 
     nop 
     call  string [mscorlib]System.Console::ReadLine() 
     pop 
     ret 
    } // end of method Program::Main 
} // end of class Console1.Program 

मैं नहीं के रूप में वस्तु संदर्भ बेस की एक वस्तु की ओर इशारा करते है चलाने के लिए इस कोड उम्मीद कर रहा था फेरबदल और कोई जिस तरह मूल वस्तु की विधि तालिका में व्युत्पन्न वर्ग में परिभाषित विधि एम() के लिए एक प्रविष्टि होगी।

लेकिन जादुई रूप से यह कोड Derived.m() को निष्पादित करता है !!

तो, वहाँ दो सवाल इसके बाद के संस्करण कोड में मुझे समझ नहीं आता हैं:

  1. क्या प्रकार के महत्व को नीचे आईएल कोड में निर्दिष्ट किया जाता है? मैंने इसे विभिन्न प्रकारों (जैसे सिस्टम.एक्सप्शन !!) में बदलकर प्रयोग करने की कोशिश की है और कोई त्रुटि रिपोर्ट नहीं की गई है। क्यूं कर??

    .locals init ([0] वर्ग Console1.Base घ)

  2. वास्तव में किस प्रकार callvirt है काम करता है? कॉल derived.m() पर कैसे पहुंचा?

अग्रिम धन्यवाद !!

सादर, अजय

+0

@ulrichb: मुझे नहीं लगता कि वह ऐसा कर सकता है। यह 'बेस बी = नया बेस(); ((व्युत्पन्न) बी) .m' जैसा होगा, सिवाय इसके कि वह वास्तव में कास्ट का उपयोग नहीं करता है (जो अपवाद फेंक देगा)। – CodesInChaos

+2

कोड सत्यापन योग्य है? – CodesInChaos

+0

@CodeInChaos: नहीं कोड सत्यापित करने योग्य नहीं है! PEVerify "अप्रत्याशित प्रकार पर स्टैक" त्रुटि देता है। – ajay

उत्तर

5

मेरा अनुमान है कि जिटर को पता चलता है कि Derived.m आभासी नहीं है और इस प्रकार कहीं और कहीं भी इंगित नहीं कर सकता है। तो callvirt वी-टेबल के माध्यम से कॉल के बजाए एक नल-चेक और कॉल को कम कर देता है।

Derived.m वर्चुअल बनाने का प्रयास करें। मुझे यकीन है कि यह तब फेंक देगा।

सी # कंपाइलर callvirt निर्देशों को छोड़ देता है, भले ही गैर वर्चुअल विधियों को कॉल करते समय भी यह this!=null साबित नहीं कर सकता है, इसलिए यह एक शून्य-जांच प्राप्त करता है। और जिटर उस मामले में पर्याप्त बुद्धिमान है जिसे वर्चुअल कॉल को एक सामान्य कॉल द्वारा एक निश्चित पते (या यहां तक ​​कि इनलाइन भी) के साथ प्रतिस्थापित करने के लिए पर्याप्त है।

और आपको यह जांचना चाहिए कि क्या आप कोड सत्यापित हैं या नहीं। मुझे लगता है कि यह नहीं है।

1

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

स्थानीय विवरण में प्रकार का उद्देश्य स्थानीय चर के प्रकार की घोषणा करना है। यह सत्यापित करने के लिए टाइप सत्यापनकर्ता द्वारा आवश्यक जानकारी प्रदान करता है कि स्थानीय चर पर सदस्य का उपयोग सही प्रकार की वस्तु पर चल रहा है।

कॉलवर्ट कई तरीकों से लागू किया जा सकता है। सबसे संभावित तरीका सी ++ vtables लागू होते हैं: एक ऑब्जेक्ट में फ़ंक्शन पॉइंटर्स की एक तालिका होती है। प्रत्येक समारोह तालिका में एक पूर्वनिर्धारित ऑफसेट पर स्थित है। फ़ंक्शन को कॉल करने के लिए, पूर्वनिर्धारित ऑफसेट पर पता लोड और कॉल किया जाता है। ध्यान दें कि कुछ मामलों में, यदि ऑब्जेक्ट का प्रकार ज्ञात है तो सीएलआर अतिरिक्त अनुकूलन कर सकता है। चाहे यह किया गया हो, मुझे नहीं पता।

1

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

हाँ, आईएल सत्यापनकर्ता यहां किसी भी अंक को स्कोर नहीं कर रहा है। आप Derived.m() विधि टिंकर को उस क्षेत्र के साथ अधिक दिलचस्प बना सकते हैं जिसे केवल व्युत्पन्न में घोषित किया गया है। मैंने बहुत अधिक प्रतिबिंब देखा है। AccessViolation के साथ कोड क्रैश को इस से आश्चर्यचकित करने के लिए अनुमति दें। हालांकि यह जानबूझकर हो सकता है, आईएल को सत्यापित करने की कोई आवश्यकता नहीं है जो किसी भी तरह दुर्घटनाग्रस्त हो। निश्चित नहीं है, इस प्रकार के सत्यापन की कमी का शोषण करना अभी तक आम नहीं है। शुक्र है।

+0

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

+0

नहीं, यह एक डीबगर के साथ भी होगा। पाठ्यक्रम की जांच करने में आसान है। –

2

आपका कोड सत्यापित नहीं है (इसे peverify के माध्यम से चलाएं)। मैंने blog post लिखा है कि कैसे कॉलवर्ट अंडर-द-हूड काम करता है जो आपको यह समझने में मदद कर सकता है कि यह क्या करता है, और आपका कोड कैसे निष्पादित करता है।

ध्यान रखें कि सीएलआर सामान्य कार्यक्रम के रूप में चलाने पर गैर-सत्यापन योग्य कोड निष्पादित करने का प्रयास करता है; केवल अगर यह वास्तव में एक समस्या का कारण बनता है यह बोर्क करता है।

आपके उदाहरण में, Base के उदाहरण पर Derived.m() पर कॉल करना काम करता है क्योंकि ऑब्जेक्ट इंस्टेंस के वास्तविक रन-टाइम बाइनरी प्रस्तुति समान है; this ऑब्जेक्ट मूल रूप से वही है, और ऑब्जेक्ट्स का कोई उदाहरण फ़ील्ड एक्सेस नहीं किया जाता है।

दोनों तरीकों में एक उदाहरण क्षेत्र पहुँच डालने की कोशिश करो और देखो क्या होता है ...

+0

धन्यवाद धन्यवाद! लेकिन मुझे संदेह है कि कोड कोडकैस द्वारा सुझाए गए जिट कंपाइलर द्वारा अनुकूलन के कारण कोड चलाता है – ajay

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