2011-10-28 8 views
9

के साथ प्रतिनिधि में लैम्ब्डा अभिव्यक्ति के परिणाम संकलित करते समय जब मैं अभिव्यक्ति वृक्ष से प्रतिनिधि बनाने के लिए Expression.Lambda(...).Compile() का उपयोग करता हूं, तो परिणाम एक प्रतिनिधि होता है जिसमें पहला तर्क Closure होता है।क्लोजर तर्क

public static Func<T, T, T> CreateTest<T>() 
{ 
    ParameterExpression a = Expression.Parameter(typeof(T)); 
    ParameterExpression b = Expression.Parameter(typeof(T)); 
    Expression addition = Expression.Add(a, b); 

    return (Func<T, T, T>)Expression.Lambda(addition, a, b).Compile(); 
} 

... 

// 'addition' equals 
// Int32 lambda_method(
//  System.Runtime.CompilerServices.Closure, 
//  Int32, 
//  Int32) 
Func<int, int, int> addition = DelegateHelper.CreateTest<int>(); 
int result = addition(5, 5); 

मैं आसानी से एक Closure वस्तु गुजर बिना साधारण कोड के माध्यम से प्रतिनिधि कॉल कर सकते हैं, लेकिन जहां इस Closure से आता है?

मैं इस प्रतिनिधि को गतिशील रूप से कैसे कॉल कर सकता हूं?

// The following does not work. 
// Exception: MethodInfo must be a runtime MethodInfo object.  
MethodInfo additionMethod = addition.Method; 
int result = (int)additionMethod.Invoke(null, new object[] { 5, 5 }); 

अभिव्यक्ति के पेड़ का उपयोग यह है कि मैं Closure वस्तु पारित करने के लिए है लग रहा है।

PropertyInfo methodProperty 
    = typeof(Delegate).GetProperty("Method", typeof(MethodInfo)); 
MemberExpression getDelegateMethod 
    = Expression.Property(Expression.Constant(addition), methodProperty); 
Func<MethodInfo> getMethodInfo 
    = (Func<MethodInfo>)Expression.Lambda(getDelegateMethod).Compile(); 
// Incorrect number of arguments supplied for call to method 
// 'Int32 lambda_method(System.Runtime.CompilerServices.Closure, Int32, Int32)' 
Expression call 
    = Expression.Call(
     getMethodInfo(), 
     Expression.Constant(5), Expression.Constant(5)); 

इस सरल उदाहरण से जो अपने आप में कोई मतलब नहीं है है। जो मैं वास्तव में प्राप्त करने की कोशिश कर रहा हूं वह है लपेटने में सक्षम होना Func<Action<object>> के साथ। मैं पहले से ही गैर घोंसले प्रतिनिधियों के लिए ऐसा कर सकता हूं। यह प्रतिबिंब के दौरान उपयोगी है, as discussed here

मुझे इस Closure ऑब्जेक्ट को सही तरीके से कैसे प्रारंभ करना चाहिए, या मैं इसे वहां से कैसे रोकूं?

+2

क्या आप एक छोटा लेकिन पूरा * उदाहरण दे सकते हैं? यह वास्तव में स्पष्ट नहीं है कि समस्या क्या है। –

+1

@ जोनस्केट: मैं अपनी पूरी कोशिश करूंगा, समस्या यह है कि कुल उदाहरण काफी जटिल है। मैं पहले से संकलित प्रतिनिधि को दोबारा कॉल करने का प्रयास कर रहा हूं। जबकि मैं समस्या का एक छोटा सबसेट निकालने का प्रयास करता हूं, [यहां] (http://pastebin.com/53f0VqnF) आप पहले से ही संपूर्ण फ़ंक्शन पा सकते हैं। –

+0

हाँ, इसे कम करने में निश्चित रूप से मदद मिलेगी :) –

उत्तर

9

Closure प्रकार जो आप देखते हैं वह एक कार्यान्वयन विवरण है। MSDN इसके बारे में बहुत स्पष्ट है:

यह API .नेट फ्रेमवर्क बुनियादी सुविधाओं का समर्थन करता है और अपने कोड से सीधे इस्तेमाल किया जा करने का इरादा नहीं है। गतिशील रूप से जेनरेट की गई विधि की रनटाइम स्थिति का प्रतिनिधित्व करता है।

एक अभिव्यक्ति वृक्ष में एक राज्य हो सकता है।

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

इसे प्राप्त करने के लिए, अभिव्यक्ति वृक्ष कंपाइलर एक प्यारा छोटी चाल का उपयोग करता है। यह DynamicMethod का उपयोग कर मेमोरी कोड में उत्पन्न होता है, जो परिभाषा स्थिर है। फिर भी, वे एक प्रतिनिधि बना रहे हैं जो “closed over its first argument” है। इसका मतलब है कि सीएलआर प्रतिनिधि के लक्ष्य क्षेत्र को स्थैतिक विधि के पहले तर्क के रूप में पास कर देगा, इसलिए आपको यह नहीं करना है। प्रभावी रूप से आप से क्लोजर तर्क छुपाएं।

आपकी समस्या का हल आसान है,, विधि कॉल प्रतिनिधि आह्वान, या तो Delegate.DynamicInvoke का उपयोग करते समय आप प्रतिबिंब का उपयोग कर रहे हैं, या एक अभिव्यक्ति पेड़ के संदर्भ में Expression.Invoke द्वारा की कोशिश न करें।

+0

यह बहुत अच्छा है, धन्यवाद! मेरा फ़ंक्शन एक नए 'MethodInfo' के साथ स्वयं को कॉल करके रिकर्सिव रूप से काम कर रहा था। मैंने एक नया बनाया जो इसके बजाय एक प्रतिनिधि को स्वीकार करता है और इसे कॉल करने के लिए 'Expression.Invoke' का उपयोग करता है। कुछ प्रमुख रिफैक्टरिंग के बाद मैं परिवर्तन कर दूंगा। अब मैं सफलतापूर्वक लपेट सकता हूं उदा। 'Func > 'Func >' के साथ। मजबूत टाइप किए गए enums के साथ जेनेरिक गणना। :) इसे पसंद करता हूँ। 'IEnumerable setFlags = EnumHelper .GetFlaggedValues ​​(झंडे);' –

+0

के, यह एनईटी 4.0 में 'एनम' का उपयोग करके भी संभव है, लेकिन मैं अन्य परिदृश्यों में इस 'प्रतिनिधि रैपर' का उपयोग कर सकता हूं। मैंने 'एनम' के साथ एक छोटी दक्षता तुलना की और अंतर नगण्य है। –