2011-04-22 9 views
17

मैं एक लाइब्रेरी पर काम कर रहा हूं जो उपयोगकर्ताओं को मनमाने ढंग से अभिव्यक्तियों को इनपुट करने की अनुमति देता है। मेरी लाइब्रेरी तब उन अभिव्यक्तियों को एक प्रतिनिधि में एक बड़ी अभिव्यक्ति के हिस्से के रूप में संकलित करती है। अब, Compile के साथ अभिव्यक्ति को संकलित करने के अभी भी अज्ञात कारणों के लिए कभी-कभी/अक्सर कोड में परिणाम होता है जो उससे धीमा होता है यदि यह संकलित अभिव्यक्ति नहीं होता है। I asked a question about this पहले और एक वर्कअराउंड Compile का उपयोग नहीं करना था, लेकिन CompileToMethod और नई गतिशील असेंबली में एक नए प्रकार पर static विधि बनाएं। वह काम करता है और कोड तेज़ है।.NET: गतिशील असेंबली से गैर-सार्वजनिक सदस्यों तक पहुंच

लेकिन उन इनपुट कर सकते हैं मनमाना भाव और यह है कि उपयोगकर्ता एक गैर सरकारी समारोह कॉल या अभिव्यक्ति में एक गैर सरकारी क्षेत्र तक पहुँचता है, यह एक System.MethodAccessException (एक गैर सरकारी विधि के मामले में) फेंकता है पता चला है जब प्रतिनिधि को बुलाया जाता है।

मैं शायद यहां क्या कर सकता हूं एक नया ExpressionVisitor बनाता है जो जांचता है कि अभिव्यक्ति गैर-सार्वजनिक पहुंचती है और उन मामलों में धीमी Compile का उपयोग करती है, लेकिन मुझे लगता है कि गतिशील असेंबली किसी भी तरह से पहुंच का अधिकार प्राप्त करेगी गैर-सार्वजनिक सदस्य। या पता लगाएं कि क्या कुछ भी है जो मैं Compile धीमा (कभी-कभी) होने के बारे में कर सकता हूं।

पूर्ण कोड इस समस्या को पुन: पेश करने:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Linq.Expressions; 
using System.Reflection; 
using System.Reflection.Emit; 

namespace DynamicAssembly 
{ 
    public class Program 
    { 
    private static int GetValue() 
    { 
     return 1; 
    } 

    public static int GetValuePublic() 
    { 
     return 1; 
    } 

    public static int Foo; 

    static void Main(string[] args) 
    { 
     Expression<Func<int>> expression =() => 10 + GetValue(); 

     Foo = expression.Compile()(); 

     Console.WriteLine("This works, value: " + Foo); 

     Expression<Func<int>> expressionPublic =() => 10 + GetValuePublic(); 

     var compiledDynamicAssemblyPublic = (Func<int>)CompileExpression(expressionPublic); 

     Foo = compiledDynamicAssemblyPublic(); 

     Console.WriteLine("This works too, value: " + Foo); 

     var compiledDynamicAssemblyNonPublic = (Func<int>)CompileExpression(expression); 

     Console.WriteLine("This crashes"); 

     Foo = compiledDynamicAssemblyNonPublic(); 
    } 

    static Delegate CompileExpression(LambdaExpression expression) 
    { 
     var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(
     new AssemblyName("MyAssembly"+ Guid.NewGuid().ToString("N")), 
     AssemblyBuilderAccess.Run); 

     var moduleBuilder = assemblyBuilder.DefineDynamicModule("Module"); 

     var typeBuilder = moduleBuilder.DefineType("MyType", TypeAttributes.Public); 

     var methodBuilder = typeBuilder.DefineMethod("MyMethod", 
     MethodAttributes.Public | MethodAttributes.Static); 

     expression.CompileToMethod(methodBuilder); 

     var resultingType = typeBuilder.CreateType(); 

     var function = Delegate.CreateDelegate(expression.Type, 
     resultingType.GetMethod("MyMethod")); 

     return function; 
    } 
    } 
} 
+0

मेरे पास आपके लिए कोई जवाब नहीं है, लेकिन निजी तरीकों को कॉल करने का समर्थन करना क्यों आवश्यक है? – jlew

+0

क्योंकि उपयोगकर्ता अपेक्षा करता है कि यह संभव होना चाहिए। क्योंकि वे अभिव्यक्ति बनाता है, जब वे अभिव्यक्ति बनाता है, जैसे '() => CallPrivateMethod() ', लेकिन वे रनटाइम पर असफल हो जाएंगे। उसके पास कुछ भी नहीं है जो इंगित करेगा कि यह तब तक काम नहीं करता जब तक वह इसे चलाता है और यह दुर्घटनाग्रस्त हो जाता है और जलता है। यह वास्तव में बुरा है और "कम से कम आश्चर्य" के नियम का उल्लंघन करता है, इसलिए मैं ऐसा करने का औचित्य सिद्ध नहीं कर सकता और मुझे धीमे कोड के लिए बसना होगा। – JulianR

+0

समझ में आता है, यदि उपयोगकर्ता एक सी # प्रोग्रामर है (उदाहरण के लिए किसी रूप में अभिव्यक्ति टाइप करने वाले किसी के विपरीत)। क्या आपने संकलित प्रतिनिधि के लिए रिलीज बनाम डीबग मोड बेंचमार्क किया है? वे एक दूसरे के साथ तुलना कैसे करते हैं? – jlew

उत्तर

5

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

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

यहां आपके परीक्षण मामले से लिया गया एक उदाहरण है।यह विफल रहता है:

Expression<Func<int>> expression =() => 10 + GetValue(); 

लेकिन यह सफल होगा:

Expression<Func<int>> expression =() => 10 + (int)typeof(Program).GetMethod("GetValue", BindingFlags.Static | BindingFlags.NonPublic).Invoke(null, null); 

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

1

मैं एक बार एक मुद्दा DynamicMethod उपयोग करते हुए उत्पन्न आईएल कोड से एक वर्ग के निजी तत्वों, तक पहुँचने के लिए किया था।

यह पता चला वर्ग के निर्माता की एक अधिभार DynamicMethod कि जो निजी उपयोग में वर्ग के प्रकार प्राप्त करता है की अनुमति दी जाएगी था:

http://msdn.microsoft.com/en-us/library/exczf7b9.aspx

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

अभिव्यक्ति पेड़ संकलित करते समय कुछ प्रकार की समान चीज हो सकती है ... या आप उस अभिव्यक्ति वृक्ष को एक डायनामिक मोड के रूप में बना सकते हैं।

+0

धन्यवाद। लेकिन 'CompileToMethod' के पास केवल एक ही तरीका है: इसे' विधिबिल्डर 'का एक उदाहरण पारित करने की आवश्यकता है। और आप केवल 'टाइपबिल्डर' से 'मेथडबिल्डर' प्राप्त कर सकते हैं और इसके लिए आपको 'मॉड्यूलबिल्डर' की आवश्यकता है और * * * आपको 'असेंबलीबिल्डर' की आवश्यकता है। – JulianR

1

गैर गतिशील विधानसभा आप द्वारा बनाया गया है, तो का उपयोग कर यह नहीं कर सकते, आप वास्तव में गतिशील विधानसभा (यहां तक ​​कि एक मजबूत नाम के साथ काम करता है के लिए एक InternalsVisibleTo शामिल कर सकते हैं)। इससे आंतरिक सदस्यों का उपयोग करने की अनुमति मिल जाएगी, जो आपके मामले में पर्याप्त हो सकती हैं?

जायज़ा लेने के लिए, यहां एक उदाहरण है जो एक और विधानसभा से आंतरिक सामान का उपयोग करने के Moq के गतिशील विधानसभा सक्षम करने के लिए गर्म से पता चलता है: http://blog.ashmind.com/2008/05/09/mocking-internal-interfaces-with-moq/

यदि यह उपाय पर्याप्त नहीं है, मैं एक संयोजन के साथ जाना चाहते हैं रिक और मिगुएल के सुझावों के लिए: एक गैर-सार्वजनिक सदस्य को प्रत्येक आमंत्रण के लिए "प्रॉक्सी" डायनामिक मोड्स बनाएं और अभिव्यक्ति वृक्ष को बदलें ताकि मूल आविष्कारों के बजाय उनका उपयोग किया जा सके।