2012-12-06 14 views
11

हमारे उत्पाद में, हमारे पास "सेवाएं" नामक चीजें हैं जो उत्पाद के विभिन्न हिस्सों (और विशेष रूप से भाषाओं-इन-हाउस भाषा, सी, पायथन और .NET) के बीच संचार का मूल माध्यम हैं।मैं अपना खुद का प्रकार कैसे बदल सकता हूं?

वर्तमान में, कोड इस तरह है (Services.Executeparams object[] args उपयोग):

myString = (string)Services.Execute("service_name", arg1, arg2, ...); 

मैं नहीं बल्कि इस तरह कोड लिखने और प्रकार की जाँच और कम वर्बोज़ कोड के लाभ प्राप्त करने में सक्षम होना चाहते हैं:

myString = ServiceName(arg1, arg2, ...); 

यह एक साधारण समारोह के साथ प्राप्त किया जा सकता है,

public static string ServiceName(int arg1, Entity arg2, ...) 
{ 
    return (string)Services.Execute("service_name", arg1, arg2, ...); 
} 

लेकिन यह वर्बोज़ है, और कई तरह की सेवाओं के लिए इसे करने के दौरान प्रबंधन करना बहुत आसान नहीं है, जैसा कि मैं करना चाहता हूं।

देखकर कैसे extern और DllImportAttribute काम करते हैं, मुझे आशा है कि यह इस तरह की कुछ भी तरह से इस हुक करने के लिए संभव होना चाहिए:

[ServiceImport("service_name")] 
public static extern string ServiceName(int arg1, Entity arg2, ...); 

लेकिन मैं कैसे सब पर इस लक्ष्य को हासिल करने के लिए जानते हैं और कर सकते हैं नहीं है ' प्रतीत होता है कि इसके लिए कोई दस्तावेज नहीं है (extern एक काफी अस्पष्ट परिभाषित मामला प्रतीत होता है)। मैंने जो सबसे नज़दीकी पाया है वह कुछ हद तक संबंधित प्रश्न है, How to provide custom implementation for extern methods in .NET? जिसने वास्तव में मेरे प्रश्न का उत्तर नहीं दिया और वैसे भी कुछ अलग है। सी # भाषा विशिष्टता (विशेष रूप से, संस्करण 4.0 में, खंड 10.6.7, बाहरी तरीकों) मदद नहीं करता है।

तो, मैं बाहरी तरीकों का एक कस्टम कार्यान्वयन प्रदान करना चाहता हूं; क्या यह हासिल किया जा सकता है? और यदि हां, तो कैसे?

+0

ऐसा करने का सबसे आम तरीका इंटरफेस और प्रॉक्सी रिमोटिंग के साथ है। – leppie

+0

देखें http://stackoverflow.com/questions/7245507/how-to-provide-custom-implementation-for-extern-methods-in-net –

+0

@PaulZahra: मैंने यह प्रश्न देखा है - मैंने इसे अपने प्रश्न में संदर्भित किया है –

उत्तर

4

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

class Program { 
     static void Main(string[] args) { 
      foo(); 
     } 

     class FooBar : Attribute { } 

     [FooBar] 
     static extern void foo(); 
    } 

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

आप इसे SSCLI20 distribution, clr/src/md/compiler/custattr.cpp स्रोत कोड फ़ाइल, regMeta :: _ हैंडलकेन CustomAttribute() फ़ंक्शन में जिटर के लिए स्रोत कोड में देख सकते हैं। यह कोड है जो .NET 2.0 के लिए सटीक है, मुझे इसके अतिरिक्त जोड़ों से अवगत नहीं है जो विधि कॉलिंग को प्रभावित करते हैं। आप इसे निम्न विशेषताओं है कि विधि कॉल के लिए पीढ़ी कोड से संबंधित से निपटने देखेंगे, तरह कि निर्वासन कीवर्ड का उपयोग करेगा:

  • [DllImport], आप कोई संदेह नहीं है पता है कि यह

  • [MethodImpl (MethodImplOptions.InternalCall)], एक विशेषता जो कि विधियों के बजाय सीएलआर में लागू विधियों पर उपयोग की जाती है। वे सी ++ में लिखे गए हैं, सीएलआर में एक आंतरिक तालिका है जो सी ++ फ़ंक्शन से लिंक होती है। एक कैननिकल उदाहरण Math.Pow() विधि है, मैंने कार्यान्वयन विवरण this answer में वर्णित किया है। तालिका अन्यथा एक्स्टेंसिबल नहीं है, यह सीएलआर स्रोत कोड

  • [कॉमइम्पोर्ट] में हार्ड-बेक्ड है, एक विशेषता जो एक इंटरफ़ेस को अन्यत्र लागू करने के रूप में चिह्नित करती है, हमेशा एक COM सर्वर में। आप शायद ही कभी इस विशेषता को प्रोग्राम करते हैं, आप इंटरऑप लाइब्रेरी का उपयोग करेंगे जो इसके बजाय Tlbimp.exe द्वारा उत्पन्न होता है। इस विशेषता को इंटरफ़ेस की आवश्यक मार्गदर्शिका देने के लिए [Guid] विशेषता भी आवश्यक है। यह अन्यथा [DllImport] विशेषता के समान है, यह अप्रबंधित कोड पर एक पिनवोक प्रकार का कॉल उत्पन्न करता है लेकिन COM कॉलिंग सम्मेलनों का उपयोग करता है। यह निश्चित रूप से केवल ठीक से काम कर सकता है यदि आपके पास वास्तव में आपके मशीन पर आवश्यक COM सर्वर है, तो यह अन्यथा असीम रूप से एक्स्टेंसिबल है।

एक गुच्छा अधिक विशेषताएं इस समारोह में पहचाने जाते हैं लेकिन उन्हें और किसी कोड कि कहीं परिभाषित किया है बुला से संबंधित नहीं है।

तो जब तक आप अपना खुद का जिटर नहीं लिखते, बाहरी का उपयोग करके आप जो चाहते हैं उसे प्राप्त करने का एक व्यवहार्य तरीका नहीं है। यदि आप इसे किसी भी तरह से आगे बढ़ाना चाहते हैं तो आप मोनो प्रोजेक्ट पर विचार कर सकते हैं।

आम तानाना समाधान है कि शुद्ध कामयाब रहे हैं काफी हद तक भूल System.AddIn नाम स्थान, बहुत लोकप्रिय MEF ढांचे और Postsharp तरह AOP समाधान कर रहे हैं।

+0

स्पष्टीकरण और पुष्टि के लिए धन्यवाद कि यह वही नहीं करेगा जो मैं चाहता हूं। –

1

मुझे हाल ही में कुछ समान (रिलेइंग विधि कॉल) करने की आवश्यकता है। मैं रनटाइम के दौरान गतिशील रूप से अग्रेषित विधि को एक प्रकार उत्पन्न करता हूं।

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

public interface IMyService 
{ 
    [ServiceImport("service_name")] 
    string ServiceName(int arg1, string arg2); 
} 

फिर कक्षा को उत्पन्न करने के लिए कोड चलाएं जो गतिशील रूप से इस इंटरफ़ेस को लागू करता है।

// Get handle to the method that is going to be called. 
MethodInfo executeMethod = typeof(Services).GetMethod("Execute"); 

// Create assembly, module and a type (class) in it. 
AssemblyName assemblyName = new AssemblyName("MyAssembly"); 
AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run, (IEnumerable<CustomAttributeBuilder>)null); 
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("MyModule"); 
TypeBuilder typeBuilder = moduleBuilder.DefineType("MyClass", TypeAttributes.Class | TypeAttributes.Public, typeof(object), new Type[] { typeof(IMyService) }); 
typeBuilder.DefineDefaultConstructor(MethodAttributes.Public); 

// Implement each interface method. 
foreach (MethodInfo method in typeof(IMyService).GetMethods()) 
{ 
    ServiceImportAttribute attr = method 
     .GetCustomAttributes(typeof(ServiceImportAttribute), false) 
     .Cast<ServiceImportAttribute>() 
     .SingleOrDefault(); 

    var parameters = method.GetParameters(); 

    if (attr == null) 
    { 
     throw new ArgumentException(string.Format("Method {0} on interface IMyService does not define ServiceImport attribute.")); 
    } 
    else 
    { 
     // There is ServiceImport attribute defined on the method. 
     // Implement the method. 
     MethodBuilder methodBuilder = typeBuilder.DefineMethod(
      method.Name, 
      MethodAttributes.Public | MethodAttributes.Virtual, 
      CallingConventions.HasThis, 
      method.ReturnType, 
      parameters.Select(p => p.ParameterType).ToArray()); 

     // Generate the method body. 
     ILGenerator methodGenerator = methodBuilder.GetILGenerator(); 

     LocalBuilder paramsLocal = methodGenerator.DeclareLocal(typeof(object[])); // Create the local variable for the params array. 
     methodGenerator.Emit(OpCodes.Ldc_I4, parameters.Length); // Amount of elements in the params array. 
     methodGenerator.Emit(OpCodes.Newarr, typeof(object)); // Create the new array. 
     methodGenerator.Emit(OpCodes.Stloc, paramsLocal); // Store the array in the local variable. 

     // Copy method parameters to the params array. 
     for (int i = 0; i < parameters.Length; i++) 
     { 
      methodGenerator.Emit(OpCodes.Ldloc, paramsLocal); // Load the params local variable. 
      methodGenerator.Emit(OpCodes.Ldc_I4, i); // Value will be saved in the index i. 
      methodGenerator.Emit(OpCodes.Ldarg, (short)(i + 1)); // Load value of the (i + 1) parameter. Note that parameter with index 0 is skipped, because it is "this". 
      if (parameters[i].ParameterType.IsValueType) 
      { 
       methodGenerator.Emit(OpCodes.Box, parameters[i].ParameterType); // If the parameter is of value type, it needs to be boxed, otherwise it cannot be put into object[] array. 
      } 

      methodGenerator.Emit(OpCodes.Stelem, typeof(object)); // Set element in the array. 
     } 

     // Call the method. 
     methodGenerator.Emit(OpCodes.Ldstr, attr.Name); // Load name of the service to execute. 
     methodGenerator.Emit(OpCodes.Ldloc, paramsLocal); // Load the params array. 
     methodGenerator.Emit(OpCodes.Call, executeMethod); // Invoke the "Execute" method. 
     methodGenerator.Emit(OpCodes.Ret); // Return the returned value. 
    } 
} 

Type generatedType = typeBuilder.CreateType(); 

// Create an instance of the type and test it. 
IMyService service = (IMyService)generatedType.GetConstructor(new Type[] { }).Invoke(new object[] { }); 
service.ServiceName(1, "aaa"); 

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

वैकल्पिक रूप से मैं सलाह देता हूं कि आप PostSharp पर एक नज़र डालें जो आपको संकलन समय के दौरान कोड उत्पन्न करने की अनुमति देता है। हालांकि यह एक भुगतान वाणिज्यिक समाधान है।

+0

हम्म। वह तकनीक काम करेगी, लेकिन अगर यह किया जा रहा है, तो यह अन्य कोड में संकलन समय पर भी किया जा सकता है। (हमारी निर्माण प्रणाली Python स्क्रिप्ट से सी # कोड जनरेट रूप में इस तरह काम करने के लिए काफी सक्षम है और फिर इसे संकलन है, जो हिट पर्फ़ क्रम कम होगी।) धन्यवाद! –

1

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

+0

सेवाएं वास्तव में रनटाइम पर पंजीकृत होती हैं और स्वीकार्य तर्कों की पूछताछ नहीं की जा सकती है- प्रत्येक केवल तर्कों की एक सूची लेता है और जो कुछ भी उन्हें पसंद करता है वह कर सकता है (आमतौर पर "सत्यापित" फ़ंक्शन को कॉल करना, जो प्रकार चाहता है उसे निर्दिष्ट करता है, लेकिन हमेशा नहीं) । लेकिन मुझे उम्मीद है कि मैं शायद कोड पीढ़ी करूँगा। धन्यवाद! –

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