2010-02-07 14 views
16

लागू तो मैं एक delegate जो कुछ समारोह जो मैं वास्तव में जब मैं पहली बार delegate वस्तु बनाने के बारे में पता नहीं है की ओर इशारा करता है। ऑब्जेक्ट बाद में कुछ फ़ंक्शन पर सेट है।अभिव्यक्ति पेड़ों और एक प्रतिनिधि

मैं भी एक अभिव्यक्ति वृक्ष बनाना चाहता हूं जो प्रतिनिधि को एक तर्क के साथ आमंत्रित करता है (इस प्रश्न के लिए तर्क 5 हो सकता है)। यह थोड़ा सा है जिसके साथ मैं संघर्ष कर रहा हूं; नीचे दिया गया कोड दिखाता है कि मैं क्या चाहता हूं लेकिन यह संकलित नहीं करता है।

Func<int, int> func = null; 
Expression<Func<int>> expr =() => func(5); 

यह expr बन बनाता है::

() => Invoke(value(Test.Program+<>c__DisplayClass0).func, 5) 

कौन सा लगता है

Func<int, int> func = null; 
Expression expr = Expression.Invoke(func, Expression.Constant(5)); 

इस उदाहरण मैं कर सकता के लिए (यह व्यावहारिक के बाद से मैं रनटाइम पर अभिव्यक्ति के पेड़ का निर्माण करने की आवश्यकता है) कि delegatefunc उपयोग करने के लिए मतलब है, मैं value(Test.Program+<>c__DisplayClass0).func बिट का उत्पादन करने की जरूरत है।

तो, मैं एक अभिव्यक्ति पेड़ जो एक प्रतिनिधि का आह्वान कर सकते हैं?

+0

[एक्शन में लिंक] (http://www.manning.com/marguerie/) अभिव्यक्ति पेड़ पर एक विस्तृत अनुभाग है। –

उत्तर

9

ठीक है, यह पता चलता है कि यह कैसे किया जा सकता है (लेकिन यह मेरी राय में बहुत असजीला है):

Func<int, int> func = null; 
Expression<Func<int, int>> bind = (x) => func(x); 

Expression expr = Expression.Invoke(bind, Expression.Constant(5)); 

Expression<Func<int>> lambda = Expression.Lambda<Func<int>>(expr); 
Func<int> compiled = lambda.Compile(); 

Console.WriteLine(expr); 

func = x => 3 * x; 
Console.WriteLine(compiled()); 

func = x => 7 * x; 
Console.WriteLine(compiled()); 

Console.Read(); 

अनिवार्य मैं (x) => func(x); का प्रयोग कर एक समारोह है कि क्या प्रतिनिधि अंक के लिए कॉल करने के लिए। लेकिन आप देख सकते हैं कि expr अत्यधिक जटिल है।इस कारण से मैं इस जवाब को अच्छा नहीं मानता, लेकिन शायद इसे बनाया जा सकता है?

+2

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

+2

मुझे "इसे स्वयं विधि करें" देखने में रुचि होगी। मुझे कल्पना है कि इसे प्रतिबिंब से कुछ जादू की आवश्यकता होगी। स्वीकार करें? –

2

यह काम करना चाहिए:

Action<int> func = i => Console.WriteLine(i * i); 

// If func is null like in your example, the GetType() call fails, 
// so give it a body or use typeof if you know the type at compile time 
var param = Expression.Parameter(func.GetType()); 

// Call the Invoke method on the delegate, which is the same as invoking() it 
var callExpr = Expression.Call(param, func.GetType().GetMethod("Invoke"), Expression.Constant(5)); 

var lambdaExpr = Expression.Lambda<Action<Action<int>>>(callExpr, param); 

var fn = lambdaExpr.Compile(); // Compile the expression tree so it can be executed 

fn(func); // Prints 25 

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

मैंने इसे .NET4 के साथ चलाया, हालांकि मुझे आशा है कि मैंने .NET4 केवल अभिव्यक्ति सामग्री में मिश्रित नहीं किया है।

संपादित PythonPower की टिप्पणी के जवाब में:

मुझे लगता है कि आप क्या चाहते हैं (एक तर्क के रूप प्रतिनिधि में पारित नहीं) जब प्रतिनिधि ही अभिव्यक्ति के रूप में वर्णन किया गया है केवल किया जा सकता है, इस तरह:

var arg = Expression.Parameter(typeof(int), "i"); 

var multiply = Expression.Multiply(arg, arg); 

var writeln = Expression.Call(typeof(Console).GetMethod("WriteLine", 
    new[] { typeof(int) }), multiply); 

var lambda = Expression.Lambda<Action<int>>(writeln, arg); 

var compiled = lambda.Compile(); 

compiled(5); // Prints 25 

एकमात्र अन्य तरीका जिसे मैं सोच सकता हूं वह बंद करने में स्थानीय रूप से घोषित प्रतिनिधि को कैप्चर करना है, लेकिन मुझे नहीं पता कि यह कैसे करना है।

+0

इसे .NET Framework 3.5 – Kane

+0

के साथ काम करने के लिए केवल एक मामूली परिवर्तन यह मेरी इच्छा के करीब है, लेकिन मैं प्रतिनिधि को तर्क के रूप में पास नहीं करना चाहता हूं। –

+0

संपादन मानता है कि मैं फ़ंक्शन जानता हूं लेकिन अभिव्यक्ति के निर्माण के बाद तक मुझे फ़ंक्शन नहीं पता है। और समारोह जरूरी नहीं है कि एक अभिव्यक्ति स्वयं ही हो। बंद विचार विचार वादा करता है। –

13

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

Action<int> func = i => Console.WriteLine(i * i); 

var callExpr = Expression.Call(Expression.Constant(func.Target), func.Method, Expression.Constant(5)); 

var lambdaExpr = Expression.Lambda<Action>(callExpr); 
var fn = lambdaExpr.Compile(); 
fn(); // Prints 25 
+0

मुझे अभिव्यक्ति को बदलना है। कॉन्स्टेंट (func.Target) इसे काम करने के लिए शून्य के साथ। लेकिन यह उस बात से जुड़ा हुआ है जो फंक्शन फंक वर्तमान में इंगित करता है कि यह बाद में क्या इंगित कर सकता है। समस्या यह है कि किसी भी समय func अंक बनाने के लिए कोई धारणा नहीं है और यह किसी भी समय बदल सकता है। –

+0

यह लगभग आवश्यक उत्तर है, लेकिन कृपया Expression.Call से पहले पैरामीटर को हटा दें। अन्यथा, एक अपवाद है (मैंने .NET4 और .NET4.5 दोनों पर जांच की है) – ironic

+0

@ironic: जब आप परीक्षण कर रहे थे तो लक्षित विधि एक स्थिर विधि थी? यह अंतर हो सकता है। –

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