पर विचार करें: सी # हैंडल एक स्ट्रक्चर पर इंटरफ़ेस विधि को कैसे कॉल करता है?
interface I { void M(); }
struct S: I { public void M() {} }
// in Main:
S s;
I i = s;
s.M();
i.M();
और मुख्य के लिए आईएल:
.maxstack 1
.entrypoint
.locals init (
[0] valuetype S s,
[1] class I i
)
IL_0000: nop
IL_0001: ldloc.0
IL_0002: box S
IL_0007: stloc.1
IL_0008: ldloca.s s
IL_000a: call instance void S::M()
IL_000f: nop
IL_0010: ldloc.1
IL_0011: callvirt instance void I::M()
IL_0016: nop
IL_0017: ret
पहले (IL_000a
), S::M()
this
के लिए एक मान प्रकार के साथ कहा जाता है। अगला (IL_0011
), इसे संदर्भ (बॉक्स किए गए) प्रकार के साथ बुलाया जाता है।
यह कैसे काम करता है?
मैं तीन तरीके के बारे में सोच सकते हैं:
I::M
की
- दो संस्करण मूल्य/रेफरी प्रकार के लिए संकलित कर रहे हैं। Vtable में, यह एक को रेफ प्रकार के लिए संग्रहीत करता है, लेकिन स्थैतिक रूप से प्रेषित कॉल मूल्य प्रकार के लिए उपयोग करते हैं। यह बदसूरत और असंभव है, लेकिन संभव है।
- vtable में, यह एक "रैपर" विधि संग्रहीत करता है जो
this
को अनबॉक्स करता है, फिर वास्तविक विधि को कॉल करता है। यह अक्षम लगता है क्योंकि सभी विधि के तर्कों को दो कॉल के माध्यम से कॉपी करना होगा। - विशेष तर्क है जो
callvirt
में इसकी जांच करता है। और भी अक्षम: सभीcallvirt
एस (मामूली) जुर्माना लगाते हैं।
नोट 'IL_0002: बॉक्स एस'। कॉल को अनुमति देने के लिए संरचना को बॉक्स किया गया है। यद्यपि जेनेरिक के साथ इसके आसपास एक रास्ता है। – Joey
@ जॉय जो 'i i = s; 'के लिए है; पहला कॉल मान प्रकार 'ldloca.s s' – valtron
का उपयोग करता है यह गठित है और आईएल को देखकर बिल्कुल मदद नहीं मिलती है। सीएलआर में प्रेषण स्टब्स के तरीके की मूल समझ का उपयोग आवश्यक है। सीएलआर टीम के वेंस मॉरिसन ने [इस ब्लॉग पोस्ट] में काफी अच्छी तरह से बताया है (https://blogs.msdn.microsoft।com/vancem/2006/03/13/खुदाई-में-इंटरफ़ेस-कॉल-इन-निवल ढांचा-ठूंठ आधारित-प्रेषण /)। –