.NET

42

में क्लास लोडर के बराबर है क्या किसी को पता है कि .NET में "जावा कस्टम क्लास लोडर" के समतुल्य को परिभाषित करना संभव है?.NET

मुझे लगता है कि CLR, "लिबर्टी" कहा जाता है को लक्षित करता है एक नई प्रोग्रामिंग भाषा विकसित करने की प्रक्रिया में हूँ:

एक छोटे पृष्ठभूमि देने के लिए। भाषा की विशेषताओं में से एक है "टाइप कन्स्ट्रक्टर" को परिभाषित करने की इसकी क्षमता, जो संकलन समय पर संकलक द्वारा निष्पादित विधियों और आउटपुट के रूप में प्रकार उत्पन्न करती हैं। वे (वाक्य रचना "लिबर्टी" में) जेनरिक के सामान्यीकरण की तरह (भाषा उस में सामान्य जेनरिक है), और इस तरह कोड लिखने की अनुमति देने हैं:

var t as tuple<i as int, j as int, k as int>; 
t.i = 2; 
t.j = 4; 
t.k = 5; 

कहाँ "टपल" इतनी तरह परिभाषित किया गया है :

public type tuple(params variables as VariableDeclaration[]) as TypeDeclaration 
{ 
    //... 
} 

इस विशिष्ट उदाहरण में, निर्माता tuple में वीबी और सी # गुमनाम प्रकार के लिए कुछ इसी तरह प्रदान करता है।

हालांकि, अज्ञात प्रकारों के विपरीत, "tuples" के नाम हैं और सार्वजनिक विधि हस्ताक्षर के अंदर उपयोग किया जा सकता है।

इसका मतलब है कि मुझे उस प्रकार के लिए एक तरीका चाहिए जो अंततः कंपाइलर द्वारा कई असेंबली में साझा करने के लिए उत्सर्जित हो जाता है। उदाहरण के लिए, मैं

tuple<x as int> विधानसभा एक में परिभाषित चाहते tuple<x as int> के रूप में एक ही प्रकार विधानसभा बी

इस के साथ समस्या यह

में परिभाषित किया जा रहा है अंत में, ज़ाहिर है, कि विधानसभा एक और विधानसभा बी करने जा रहे हैं अलग-अलग समय पर संकलित किया जाए, जिसका अर्थ है कि वे दोनों टुपल प्रकार के अपने असंगत संस्करणों को उत्सर्जित कर देंगे।

class tuple<T> 
{ 
    public Field1 as T; 
} 

class tuple<T, R> 
{ 
    public Field2 as T; 
    public Field2 as R; 
} 

:

मैं "प्रकार विलोपन" के कुछ प्रकार का उपयोग कर तो यह है कि मैं इस तरह प्रकार के एक समूह के साथ एक साझा पुस्तकालय (यह "लिबर्टी" वाक्य रचना) के लिए होता है ऐसा करने के लिए, में देखा और फिर i, j, और k tuple फ़ील्ड से Field1, Field2, और Field3 तक पहुंच को पुनर्निर्देशित करें।

हालांकि यह वास्तव में एक व्यवहार्य विकल्प नहीं है। इसका मतलब यह होगा कि संकलन समय tuple<x as int> और tuple<y as int> अलग-अलग प्रकार के होते हैं, जबकि रनटाइम समय पर उन्हें एक ही प्रकार के रूप में माना जाएगा। इससे समानता और प्रकार की पहचान जैसी चीजों के लिए कई समस्याएं पैदा होंगी। यह मेरे स्वाद के लिए एक अमूर्तता की बहुत कमजोर है।

अन्य संभावित विकल्प "राज्य बैग ऑब्जेक्ट्स" का उपयोग करना होगा। हालांकि, एक राज्य बैग का उपयोग भाषा में "प्रकार कन्स्ट्रक्टर" के लिए समर्थन रखने के पूरे उद्देश्य को हरा देगा। संकलन समय पर नए प्रकार उत्पन्न करने के लिए "कस्टम भाषा एक्सटेंशन" को सक्षम करने का विचार है कि संकलक स्थिर प्रकार की जांच कर सकता है।

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

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

तो, क्या मैं जानना चाहता हूँ है:

क्या यह संभव है नेट, जावा में क्लास लोडर की तरह कुछ है, जहां मैं में, गैर-मौजूद प्रकार के लिए एक संदर्भ का उत्सर्जन कर सकते हैं अनुकरण करने के लिए और फिर जनरेट कोड के उपयोग से पहले रनटाइम पर उस प्रकार का संदर्भ चलता है?

नोट:

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

+0

ओह वाह, पहली पोस्ट मैंने कभी सोचा था कि अध्याय शीर्षक होना चाहिए। महान जानकारी! पोस्ट करने का शुक्रिया! –

उत्तर

51

उत्तर हाँ है, लेकिन समाधान थोड़ा मुश्किल है।

System.Reflection.Emit नामस्थान उन प्रकारों को परिभाषित करता है जो विधानसभाओं को गतिशील रूप से उत्पन्न करने की अनुमति देता है। वे उत्पन्न असेंबली को क्रमशः परिभाषित करने की अनुमति भी देते हैं। दूसरे शब्दों में गतिशील असेंबली में प्रकार जोड़ना, जेनरेट कोड को निष्पादित करना संभव है, और उसके बाद बाद में असेंबली में और प्रकार जोड़ना संभव है।

System.AppDomain कक्षा भी AssemblyResolve घटना को परिभाषित करती है जो फ्रेमवर्क असेंबली लोड करने में विफल होने पर आग लगती है। उस घटना के लिए एक हैंडलर जोड़कर, एक "रनटाइम" असेंबली को परिभाषित करना संभव है जिसमें सभी "निर्मित" प्रकार रखे जाते हैं। कंपाइलर द्वारा निर्मित कोड जो एक निर्मित प्रकार का उपयोग करता है, रनटाइम असेंबली में एक प्रकार का संदर्भ देगा। चूंकि रनटाइम असेंबली वास्तव में डिस्क पर मौजूद नहीं है, इसलिए AssemblyResolve ईवेंट पहली बार संकलित कोड को किसी प्रकार के प्रकार तक पहुंचने का प्रयास करने के लिए निकाल दिया जाएगा। घटना के लिए हैंडल तब गतिशील असेंबली उत्पन्न करेगा और इसे सीएलआर में वापस कर देगा।

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

दूसरी समस्या यह सुनिश्चित कर रही है कि संदर्भित प्रकार सभी को संदर्भित किसी भी कोड से पहले गतिशील असेंबली में डाला जाए। System.AppDomain वर्ग भी TypeResolve ईवेंट को परिभाषित करता है जिसे तब भी निष्पादित किया जाता है जब सीएलआर गतिशील असेंबली में किसी प्रकार को हल करने में असमर्थ होता है। यह ईवेंट हैंडलर को उस कोड से पहले गतिशील असेंबली के अंदर प्रकार को परिभाषित करने का मौका देता है जो इसका उपयोग करता है। हालांकि, यह घटना इस मामले में काम नहीं करेगी। सीएलआर असेंबली के लिए घटना को आग नहीं देगा जो अन्य असेंबली द्वारा "स्थाई संदर्भित" हैं, भले ही संदर्भित असेंबली गतिशील रूप से परिभाषित की गई हो। इसका मतलब यह है कि संकलित असेंबली रन में किसी भी अन्य कोड से पहले कोड चलाने के लिए हमें एक तरीका चाहिए और इसे गतिशील रूप से रनटाइम असेंबली में आवश्यक प्रकारों को इंजेक्ट करना होगा यदि उन्हें पहले ही परिभाषित नहीं किया गया है।अन्यथा जब सीएलआर ने उन प्रकारों को लोड करने का प्रयास किया तो यह ध्यान देगा कि गतिशील असेंबली में उनके प्रकार की आवश्यकता नहीं होती है और एक प्रकार लोड अपवाद फेंक देगा।

सौभाग्य से, सीएलआर दोनों समस्याओं का समाधान प्रदान करता है: मॉड्यूल प्रारंभकर्ता। एक मॉड्यूल प्रारंभकर्ता "स्थैतिक वर्ग कन्स्ट्रक्टर" के समतुल्य है, सिवाय इसके कि यह केवल एक वर्ग नहीं बल्कि पूरे मॉड्यूल को प्रारंभ करता है। Baiscally, सीएलआर होगा:

  1. मॉड्यूल के अंदर किसी भी प्रकार से पहले मॉड्यूल कन्स्ट्रक्टर चलाएं।
  2. गारंटी है कि मॉड्यूल कन्स्ट्रक्टर द्वारा सीधे उपयोग किए जाने वाले केवल उन प्रकारों को लोड किया जाएगा, जबकि यह
  3. निष्पादित करने के बाद मॉड्यूल के बाहर कोड को किसी भी सदस्य तक पहुंचने की अनुमति नहीं देता है जब तक कि कन्स्ट्रक्टर समाप्त नहीं हो जाता है।

यह कक्षा पुस्तकालयों और निष्पादन योग्य दोनों समेत सभी असेंबली के लिए करता है, और EXE के लिए मुख्य विधि निष्पादित करने से पहले मॉड्यूल कन्स्ट्रक्टर चलाएगा।

रचनाकारों के बारे में अधिक जानकारी के लिए यह blog post देखें।

किसी भी मामले में, मेरी समस्या के लिए एक पूर्ण समाधान कई टुकड़े की आवश्यकता है:,

  1. निम्नलिखित वर्ग परिभाषा, एक "भाषा रनटाइम dll" अंदर परिभाषित कि संकलक द्वारा उत्पादित सभी विधानसभाओं द्वारा संदर्भित है (यह सी # कोड है)।

    using System; 
    using System.Collections.Generic; 
    using System.Reflection; 
    using System.Reflection.Emit; 
    
    namespace SharedLib 
    { 
        public class Loader 
        { 
         private Loader(ModuleBuilder dynamicModule) 
         { 
          m_dynamicModule = dynamicModule; 
          m_definedTypes = new HashSet<string>(); 
         } 
    
         private static readonly Loader m_instance; 
         private readonly ModuleBuilder m_dynamicModule; 
         private readonly HashSet<string> m_definedTypes; 
    
         static Loader() 
         { 
          var name = new AssemblyName("$Runtime"); 
          var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(name, AssemblyBuilderAccess.Run); 
          var module = assemblyBuilder.DefineDynamicModule("$Runtime"); 
          m_instance = new Loader(module); 
          AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve); 
         } 
    
         static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) 
         { 
          if (args.Name == Instance.m_dynamicModule.Assembly.FullName) 
          { 
           return Instance.m_dynamicModule.Assembly; 
          } 
          else 
          { 
           return null; 
          } 
         } 
    
         public static Loader Instance 
         { 
          get 
          { 
           return m_instance; 
          } 
         } 
    
         public bool IsDefined(string name) 
         { 
          return m_definedTypes.Contains(name); 
         } 
    
         public TypeBuilder DefineType(string name) 
         { 
          //in a real system we would not expose the type builder. 
          //instead a AST for the type would be passed in, and we would just create it. 
          var type = m_dynamicModule.DefineType(name, TypeAttributes.Public); 
          m_definedTypes.Add(name); 
          return type; 
         } 
        } 
    } 
    

    वर्ग, एक सिंगलटन कि गतिशील विधानसभा कि निर्माण प्रकार में। यह भी एक "हैश सेट" कि कि पहले से ही गतिशील रूप से उत्पन्न कर दिया है प्रकारों का सेट संग्रहीत करता रखती बनाया जाएगा के लिए एक संदर्भ रखती है परिभाषित करता है और अंततः एक सदस्य को परिभाषित करता है जिसका उपयोग इस प्रकार को परिभाषित करने के लिए किया जा सकता है। यह उदाहरण सिर्फ एक सिस्टम देता है। Reflection.Emit.TypeBuilder उदाहरण जिसका उपयोग तब उत्पन्न होने वाले वर्ग को परिभाषित करने के लिए किया जा सकता है। एक वास्तविक प्रणाली में, विधि शायद कक्षा के एएसटी प्रतिनिधित्व में ले जाएगी, और केवल पीढ़ी को स्वयं ही करें।

  2. संकलित विधानसभाओं कि निम्न दो संदर्भ फेंकना (ILASM वाक्य रचना में दिखाया गया है):

    .assembly extern $Runtime 
    { 
        .ver 0:0:0:0 
    } 
    .assembly extern SharedLib 
    { 
        .ver 1:0:0:0 
    } 
    

    यहाँ "SharedLib" भाषा का पूर्वनिर्धारित क्रम पुस्तकालय है कि "लोडर" श्रेणी से ऊपर और "$ क्रम में परिभाषित किया गया है शामिल "गतिशील रनटाइम असेंबली है जिसे संरक्षित प्रकारों में डाला जाएगा।

  3. भाषा में संकलित प्रत्येक असेंबली के अंदर एक "मॉड्यूल कन्स्ट्रक्टर"।

    जहां तक ​​मुझे पता है, वहां कोई .NET भाषा नहीं है जो मॉड्यूल कन्स्ट्रक्टर्स को स्रोत में परिभाषित करने की अनुमति देती है। सी ++/सीएलआई कंपाइलर एकमात्र संकलक है जिसे मैं जानता हूं जो उन्हें उत्पन्न करता है। आईएल में, वे इस तरह दिखेगा, मॉड्यूल में और किसी भी प्रकार की परिभाषाओं के अंदर नहीं सीधे परिभाषित:

    .method privatescope specialname rtspecialname static 
         void .cctor() cil managed 
    { 
        //generate any constructed types dynamically here... 
    } 
    

    मेरे लिए, यह एक समस्या यह है कि मैं कस्टम आईएल लिखने के लिए यह काम करने के लिए प्राप्त करने के लिए नहीं है। मैं एक कंपाइलर लिख रहा हूं, इसलिए कोड जनरेशन कोई मुद्दा नहीं है।

    एक विधानसभा कि प्रकार tuple<i as int, j as int> और tuple<x as double, y as double, z as double> इस्तेमाल किया मॉड्यूल निर्माता की तरह प्रकार उत्पन्न करने के लिए की आवश्यकता होगी के मामले में निम्नलिखित (सी # वाक्य रचना में यहाँ):

    class Tuple_i_j<T, R> 
    { 
        public T i; 
        public R j; 
    } 
    
    class Tuple_x_y_z<T, R, S> 
    { 
        public T x; 
        public R y; 
        public S z; 
    } 
    

    टपल कक्षाएं सामान्य प्रकार के रूप में उत्पन्न कर रहे हैं पहुंच के मुद्दों को पाने के लिए। इससे संकलित असेंबली में कोड tuple<x as Foo> का उपयोग करने की अनुमति होगी, जहां फू कुछ गैर-सार्वजनिक प्रकार था।

    मॉड्यूल निर्माता है कि ऐसा किया के शरीर (यहाँ केवल एक ही प्रकार दिखा रहा है, और सी # वाक्य रचना में लिखा) होगा इस तरह दिखेगा: किसी भी मामले में

    var loader = SharedLib.Loader.Instance; 
    lock (loader) 
    { 
        if (! loader.IsDefined("$Tuple_i_j")) 
        { 
         //create the type. 
         var Tuple_i_j = loader.DefineType("$Tuple_i_j"); 
         //define the generic parameters <T,R> 
         var genericParams = Tuple_i_j.DefineGenericParameters("T", "R"); 
         var T = genericParams[0]; 
         var R = genericParams[1]; 
         //define the field i 
         var fieldX = Tuple_i_j.DefineField("i", T, FieldAttributes.Public); 
         //define the field j 
         var fieldY = Tuple_i_j.DefineField("j", R, FieldAttributes.Public); 
         //create the default constructor. 
         var constructor= Tuple_i_j.DefineDefaultConstructor(MethodAttributes.Public); 
    
         //"close" the type so that it can be used by executing code. 
         Tuple_i_j.CreateType(); 
        } 
    } 
    

तो, यह तंत्र था मैं सीएलआर में कस्टम क्लास लोडर के किसी न किसी समकक्ष को सक्षम करने के लिए आया था।

क्या किसी को ऐसा करने का एक आसान तरीका पता है?

+0

उह, सामान्य मॉड्यूल कन्स्ट्रक्टर से आपकी मॉड्यूल कन्स्ट्रक्टर परिभाषा अलग कैसे है? 'निजी hidebysig' के विपरीत' privatescope' का उपयोग करने में अंतर क्या है? –

+0

आह, बस इसे समझ लिया। मॉड्यूल कोक्टर को छोड़कर कोई अंतर किसी विशेष प्रकार में नहीं रखा गया है। पता नहीं था कि आप यह भी कर सकते हैं :) –

-5

मुझे लगता है कि यह डीएलआर को सी # 4.0 में प्रदान करने वाली चीज़ है। अभी तक जानकारी से आने के लिए मुश्किल है, लेकिन शायद हम पीडीसी 08 पर और जानेंगे। उत्सुकता से आपके सी # 3 समाधान को देखने का इंतजार कर रहा है ... मुझे लगता है कि यह अनाम प्रकारों का उपयोग करता है।

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