2012-07-10 23 views
16

होती है जब delegate कीवर्ड सी # में उपयोग किया जाता है, सी # कंपाइलर स्वचालित रूप से System.MulticastDelegate कक्षा से व्युत्पन्न कक्षा उत्पन्न करता है।प्रतिनिधि कीवर्ड के लिए कंपाइलर जेनरेटेड सीलबंद क्लास में आभासी विधियां

इस कंपाइलर जेनरेट क्लास में 3 विधियां भी हैं: Invoke, BeginInvoke and EndInvoke

इन तीनों तरीकों को public virtual extern चिह्नित किया गया है लेकिन दिलचस्प रूप से कक्षा को sealed चिह्नित किया गया है।

एक सीलबंद कक्षा में परिभाषित वर्चुअल विधियां न केवल प्रतिद्वंद्वी के रूप में आती हैं बल्कि वास्तव में सी # में अवैध होती हैं।

तो मेरा सवाल यह है कि क्या इसका कोई विशिष्ट कारण है या क्या यह उन कुछ हानिकारक चीजों में से एक है जो कुछ अनुमानित भविष्य में वृद्धि को ध्यान में रखते हुए किया जाता है?

संपादित करें 1:

कारण के रूप में 'कॉल' के लिए इतना है कि प्रतिनिधि वस्तु हमेशा CLR से अशक्त के लिए चेक किया गया है तीन में से किसी को निष्पादित करने के प्रयास करने से पहले विरोध किया 'callVirt' आईएल opcode के उपयोग कराने के लिए किया जा सकता है तरीकों? हालांकि मैं यह देखने में असफल रहा कि delegate इस संबंध में एक विशेष मामला क्यों होना चाहिए।

इसके अलावा यह एक प्रदर्शन हिट (हालांकि यह मामूली हो सकता है) callvirt का उपयोग कराने के लिए

संपादित 2 नहीं है:

जोड़ा गया सीआईएल टैग, यह पता चला है के रूप में है कि प्रतिनिधियों को परिभाषित करने के सी # तरीका वास्तव में सीआईएल मानक द्वारा अनिवार्य है। मानक राज्यों (निम्नलिखित पूर्ण पाठ नहीं है)

प्रतिनिधियों के पास मूल प्रकार का सिस्टम होगा। डिलीगेट। प्रतिनिधियों को घोषित किया जाएगा सील, और केवल प्रतिनिधि ही या तो निर्दिष्ट पहले दो या सभी चार विधियां हैं। ये विधियों को रनटाइम घोषित किया जाएगा और प्रबंधित किया जाएगा। उनके पास शरीर नहीं होगा, क्योंकि वह निकाय VES द्वारा स्वचालित रूप से बनाया जाएगा। अन्य प्रतिनिधियों पर उपलब्ध विधियों को कक्षा सिस्टम से विरासत में मिला है। बेस क्लास लाइब्रेरी में डिलीगेट करें। प्रतिनिधि तरीके हैं:

  1. उदाहरण निर्माता
  2. आह्वान विधि होगी आभासी
  3. BeginInvoke विधि, वर्तमान है, किया जाएगा आभासी
  4. EndInvoke विधि आभासी किया जाएगा

तो यह निश्चित रूप से संकलक प्रक्रिया का दुष्प्रभाव नहीं है या अन्य रोचक कंपाइलर आउटपुट के समान है।

यदि मानक कुछ पर जोर देता है, तो यह कुछ अच्छे कारण और तर्क के लिए होना चाहिए।

तो अब सवाल यह है कि प्रतिनिधियों के लिए सीआईएल मानक एक ही समय में मुहरबंद और आभासी पर जोर क्यों देता है?के बाद से है कि शरीर वीईएस द्वारा स्वचालित रूप से बनाया किया जाएगा

, पकड़ यहाँ झूठ ?:

वे एक शरीर नहीं होगा करता है।

क्या वे आभासी चिह्नित हैं ताकि वीईएस/सीएलआर उत्पन्न शरीर को इन तरीकों के आविष्कार पर निष्पादित किया जा सके?

+8

भूलें, सी # में मान्य नहीं है आईएल में पूरी तरह से मान्य हो सकता है - वर्तमान में इसके अन्य उदाहरण भी हैं, यह संभवतः एक और है। लेकिन दिलचस्प खोज के लिए +1। –

+0

मैंने कोशिश की कि ILDasm और ओवरराइड कीवर्ड के साथ आईएल में आभासी में अनुवाद किया गया है। – MBen

+0

@MBen हां लेकिन सी # जेनरेटेड प्रतिनिधि वर्ग के लिए ओवरराइड करने के लिए कोई संबंधित बेस क्लास विधियां नहीं हैं। इसलिए यहां वर्चुअल कीवर्ड 'ओवरराइड' का नतीजा नहीं है :) –

उत्तर

3

जैसा कि मैंने अपने प्रश्न में नोट किया है कि यह मुहरबंद आभासी विसंगति वास्तव में सीआईएल मानक द्वारा अनिवार्य है। यह स्पष्ट नहीं है कि सीआईएल मानक विशेष रूप से उल्लेख करता है कि , BeginInvoke और EndInvoke प्रतिनिधि विधियों को आभासी होना चाहिए जबकि एक ही समय में Delegate विरासत वर्ग को सील करने के लिए अनिवार्य होना चाहिए।

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

जब एक प्रतिनिधि के आह्वान को बुलाया जाता है, तो सीएलआर स्वचालित रूप से इस विधि के लिए एक अत्यधिक अनुकूलित निकाय उत्सर्जित करता है क्योंकि आईएल कोड को संकलित करने के विरोध में यह 'सामान्य' तरीकों के लिए होता है। आईएल में virtual चिह्नित होने के साथ इसका कोई लेना-देना नहीं है।

मैंने आईएल कोड को संशोधित करने और इसे पुनः संयोजित करके भी सत्यापित किया है कि वर्चुअल प्रतिनिधि वर्ग के आईएल कोड से वर्चुअल को सुरक्षित रूप से हटाया जा सकता है। सीआईएल मानक के उल्लंघन के बावजूद जेनरेटेड असेंबली पूरी तरह ठीक है।

.class private auto ansi beforefieldinit MainApp 
     extends [mscorlib]System.Object 
{ 
    .class auto ansi sealed nested private Echo 
     extends [mscorlib]System.MulticastDelegate 
    { 
    .method public hidebysig specialname rtspecialname 
      instance void .ctor(object 'object', 
           native int 'method') runtime managed 
    { 
    } // end of method Echo::.ctor 

    .method public hidebysig instance int32 Invoke(int32 i) runtime managed 
    { 
    } // end of method Echo::Invoke 

    .method public hidebysig instance class [mscorlib]System.IAsyncResult 
      BeginInvoke(int32 i, 
         class [mscorlib]System.AsyncCallback callback, 
         object 'object') runtime managed 
    { 
    } // end of method Echo::BeginInvoke 

    .method public hidebysig instance int32 EndInvoke(class [mscorlib]System.IAsyncResult result) runtime managed 
    { 
    } // end of method Echo::EndInvoke 

    } // end of class Echo 

    .method public hidebysig static void Main() cil managed 
    { 
    .entrypoint 
    // Code size  34 (0x22) 
    .maxstack 3 
    .locals init ([0] class MainApp app, 
      [1] class MainApp/Echo dele) 
    IL_0000: nop 
    IL_0001: newobj  instance void MainApp::.ctor() 
    IL_0006: stloc.0 
    IL_0007: ldloc.0 
    IL_0008: ldftn  instance int32 MainApp::DoEcho(int32) 
    IL_000e: newobj  instance void MainApp/Echo::.ctor(object, 
                  native int) 
    IL_0013: stloc.1 
    IL_0014: ldloc.1 
    IL_0015: ldc.i4.5 
    //callvirt can also be replaced by call without affecting functionality 
    // since delegate object is essentially not null here 
    IL_0016: callvirt instance int32 MainApp/Echo::Invoke(int32) 
    IL_001b: call  void [mscorlib]System.Console::WriteLine(int32) 
    IL_0020: nop 
    IL_0021: ret 
    } // end of method MainApp::Main 

    .method private hidebysig instance int32 
      DoEcho(int32 i) cil managed 
    { 
    // Code size  7 (0x7) 
    .maxstack 1 
    .locals init ([0] int32 CS$1$0000) 
    IL_0000: nop 
    IL_0001: ldarg.1 
    IL_0002: stloc.0 
    IL_0003: br.s  IL_0005 

    IL_0005: ldloc.0 
    IL_0006: ret 
    } // end of method MainApp::DoEcho 

    .method public hidebysig specialname rtspecialname 
      instance void .ctor() cil managed 
    { 
    // Code size  7 (0x7) 
    .maxstack 8 
    IL_0000: ldarg.0 
    IL_0001: call  instance void [mscorlib]System.Object::.ctor() 
    IL_0006: ret 
    } // end of method MainApp::.ctor 

} // end of class MainApp 

ध्यान दें कि मैंने वर्चुअल विधियों को सामान्य उदाहरण विधियों में परिवर्तित कर दिया है।

चूंकि यह बदल गया आईएल पूरी तरह से ठीक चलाता है यह साबित करता है कि मुहरबंद प्रतिनिधि वर्ग में मानक अनिवार्य वर्चुअल विधियां आवश्यक नहीं हैं। वे सामान्य उदाहरण विधियां भी हो सकती हैं।

तो सभी संभावनाओं में इस विसंगति पर जोर दिया जाता है कि इन तीन प्रतिनिधि तरीकों को बुलाए जाने के परिणामस्वरूप वास्तव में कुछ अन्य विधि (यानी रन-टाइम बहुरूपता 'सामान्य' आभासी तरीकों 'की तरह कॉल करने का परिणाम होगा या ऐसा हो गया है प्रतिनिधियों से संबंधित कुछ भविष्य के काल्पनिक वृद्धि को समायोजित करने के लिए।

+1

क्या आप मुझे संदर्भित कर सकते हैं कि आपने 'आईएल संकलन के विरोध में अत्यधिक अनुकूलित शरीर' के बारे में क्या सीखा है? –

3

यह संकलन प्रक्रिया का दुष्प्रभाव है। मुझे इसके लिए सही कारण नहीं पता है, इस तरह के व्यवहार के और उदाहरण हैं। उदाहरण के लिए, एक संकलित स्थैतिक वर्ग एक अमूर्त सीलबंद वर्ग बन जाता है (इसलिए आप इसका उदाहरण नहीं बना सकते हैं, और आप इससे प्राप्त नहीं कर सकते हैं)।

+0

स्टेटिक कक्षाओं के बारे में बहुत ही रोचक बिंदु:) ... लेकिन मुझे लगता है कि अमूर्त मुहरबंद काफी हद तक एकमात्र तरीका है स्टेटिक वर्गों का अर्थशास्त्र एमएसआईएल में प्रदर्शित किया जा सकता है 'आप कर सकते हैं इसका एक उदाहरण नहीं बनाते हैं, आप इसके साथ उत्तराधिकारी नहीं हो सकते हैं 'कक्षा के साथ ही कोई भी उदाहरण सदस्य नहीं है' स्थिर के लिए अनुबंध है। –

+0

कृपया प्रश्न के लिए मेरा संपादन 2 देखें। –

1

ऐसा लगता है कि प्रतिनिधियों के लिए विशिष्ट नहीं है। मैं इस उदाहरण की कोशिश की:

public abstract class Base 
    { 
     public abstract void Test(); 
    } 

    public sealed class Derived : Base 
    { 
     public override void Test() 
     { 
      throw new NotImplementedException(); 
     } 
    } 

और ILDASM में मैं टेस्ट() के कार्यान्वयन के लिए यह मिलता है:

.method public hidebysig virtual instance void 
     Test() cil managed 
{ 
    // Code size  7 (0x7) 
    .maxstack 8 
    IL_0000: nop 
    IL_0001: newobj  instance void [mscorlib]System.NotImplementedException::.ctor() 
    IL_0006: throw 
} // end of method Derived::Test 

हो सकता है कि ओवरराइड कीवर्ड एक CLR शब्द नहीं है।

+1

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

+0

हाँ अच्छा बिंदु। – MBen

+0

बस एक बिंदु मैंने अभी सीखा। वर्चुअल से पहले कीवर्ड 'न्यूज़लॉट' की अनुपस्थिति दर्शाती है कि वर्चुअल विधि ओवरराइड है ... –

4

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

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

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

+0

स्पष्टीकरण के लिए धन्यवाद। कृपया प्रश्न में मेरा संपादन 2 देखें। –