2013-11-26 11 views
5

के हिस्से के रूप में लैम्ब्डा अभिव्यक्ति का मूल्यांकन करें मैं अभिव्यक्ति वृक्षों का उपयोग करके लैम्ब्डा अभिव्यक्ति बनाने की कोशिश कर रहा हूं। - स्वरूपण तर्क पहले से ही का एक उदाहरण के रूप में मेरे लिए सौंप दिया हैअभिव्यक्ति वृक्ष

Func<DateTime, string> requiredLambda = dt => 
    { 
     var formattedDate = dt.ToShortDateString(); 

     /** 
     * The logic is not important here - what is important is that I 
     * am using the result of the executed lambda expression. 
     * */ 
     var builder = new StringBuilder(); 
     builder.Append(formattedDate); 
     builder.Append(" Hello!"); 
     return builder.ToString(); 
    }; 

पकड़ है कि मैं शुरू से इस पेड़ के निर्माण नहीं कर रहा हूँ है: यह लैम्ब्डा अभिव्यक्ति की प्रारूप मैं बनाने के लिए कोशिश कर रहा हूँ है Expression<Func<DateTime, string>> - कहते हैं:

Expression<Func<DateTime, string>> formattingExpression = dt => dt.ToShortDateString(); 

मुझे पता है कि अभिव्यक्ति पेड़ के बाहर मैं

formattingExpression.Compile()(new DateTime(2003, 2, 1)) 
को

कॉल कर सकते हैं अभिव्यक्ति का मूल्यांकन करें - लेकिन मुद्दा यह है कि मैं इसे अभिव्यक्ति वृक्ष के भीतर मूल्यांकन और असाइन करना चाहता हूं - मुझे अभिव्यक्ति वृक्ष के भीतर परिणाम पर अतिरिक्त तर्क करने की अनुमति देता है।

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

+0

मुझे समझ में नहीं आता है। अभिव्यक्ति वृक्ष के अंदर आप 'formattingExpression.Compile()' को क्यों कॉल नहीं कर सकते? – svick

उत्तर

3

तो, अगर मैं तुम्हें सही ढंग से समझ, आप चाहते हैं

Func<Func<DateTime,string>, DateTime, string> requiredLambda = (f, dt) => 
{ 
    var formattedDate = f.Invoke(dt); 

    /** 
    * The logic is not important here - what is important is that I 
    * am using the result of the executed lambda expression. 
    * */ 
    var builder = new StringBuilder(); 
    builder.Append(formattedDate); 
    builder.Append(" Hello!"); 
    return builder.ToString(); 
}; 

तो फिर आप अपने इनपुट अभिव्यक्ति है एक लैम्ब्डा (अभिव्यक्ति) बनाने के लिए जो आपके पास किए गए फ़ंक्शन का उपयोग करता है और इसके आसपास कुछ अतिरिक्त काम करता है। तो आप अनिवार्य रूप से सिर्फ एक अभिव्यक्ति के अंदर इस समारोह का उपयोग करना चाहते हैं।

इस बिंदु पर, मुझे यह सुझाव देने की अनुमति दें कि आप अभिव्यक्तियों का भी उपयोग नहीं करते हैं। आप केवल एक ऐसा फ़ंक्शन बना सकते हैं जो Func<DateTime, string> पैरामीटर लेता है और कुछ संसाधित करने के लिए इसका उपयोग करता है। लेकिन अगर आपको वास्तव में कुछ के लिए अभिव्यक्ति की आवश्यकता है, तो मैं बस एक बनाने के तरीके को समझाने की कोशिश करूंगा।

इस उदाहरण के लिए, मैं इस समारोह बना रहे होते हैं:

string MonthAndDayToString (int month, int day) 
{ 
    return "'" + formattingFunction(new DateTime(2013, month, day)) + "'" 
} 

आप देख सकते हैं, मैं एक Func<int, int, string> जो तब DateTime वस्तु बनाता है और समारोह और उसके बाद को पास कर बनाने के लिए जा रहा हूँ आगे परिणाम बदलता है।

Func<DateTime, string> formatting = dt => (...) // as above 

// define parameters for the lambda expression 
ParameterExpression monthParam = Expression.Parameter(typeof(int)); 
ParameterExpression dayParam = Expression.Parameter(typeof(int)); 

// look up DateTime constructor 
ConstructorInfo ci = typeof(DateTime).GetConstructor(new Type[] { typeof(int), typeof(int), typeof(int) }); 

// look up string.Concat 
MethodInfo concat = typeof(string).GetMethod("Concat", new Type[] { typeof(string), typeof(string), typeof(string) }); 

// inner call: formatting(new DateTime(2013, month, day)) 
var call = Expression.Call(formatting.Method, Expression.New(ci, Expression.Constant(2013), monthParam, dayParam)); 

// concat: "'" + call + "'" 
var expr = Expression.Call(concat, Expression.Constant("'"), call, Expression.Constant("'")); 

// create the final lambda: (int, int) => expr 
var lambda = Expression.Lambda<Func<int, int, string>>(expr, new ParameterExpression[] { monthParam, dayParam }); 

// compile and execute 
Func<int, int, string> func = lambda.Compile(); 
Console.WriteLine(func(2, 1)); // '01.02.2013 Hello!' 
Console.WriteLine(func(11, 26)); // '26.11.2013 Hello!' 

एलेक्स 'जवाब को देखने के बाद ऐसा लगता है कि मैं अपने प्रश्न को गलत समझा और आप क्या कर रहे हैं रिवर्स का समाधान करने की कोशिश की। लेकिन यह आप वास्तव में क्या करने के लिए कोशिश कर रहे हैं करने के लिए इसे बदलने के लिए सब भी अलग नहीं है:

Func<DateTime, string> formatting = dt => dt.ToShortDateString(); 

ParameterExpression param = Expression.Parameter(typeof(DateTime)); 
MethodInfo concat = typeof(string).GetMethod("Concat", new Type[] { typeof(string), typeof(string), typeof(string) }); 

var call = Expression.Call(formatting.Method, param); 
var expr = Expression.Call(concat, Expression.Constant("'"), call, Expression.Constant(" Hello!'")); 
var lambda = Expression.Lambda<Func<DateTime, string>>(expr, new ParameterExpression[] { param }); 

Func<DateTime, string> func = lambda.Compile(); 
Console.WriteLine(func(new DateTime(2013, 02, 01))); 
Console.WriteLine(func(new DateTime(2013, 11, 26))); 

लेकिन मैं अभी भी लोगों का तर्क होता है कि एक सामान्य समारोह है कि एक Func<DateTime, string> और एक DateTime पैरामीटर लेता है बनाए रखने के लिए एक बहुत आसान होगा । तो जब तक आप को वास्तव में अभिव्यक्तियों की आवश्यकता नहीं है, तो उनसे बचें।


मुझे अभी भी क्यों नहीं लगता कि आपको इसके लिए अभिव्यक्ति की आवश्यकता है। इस उदाहरण पर विचार:

private Func<DateTime, string> formatting = dt => dt.ToShortDateString(); 
private Func<DateTime, string> formattingLogic = null; 

public Func<DateTime, string> FormattingLogic 
{ 
    get 
    { 
     if (formattingLogic == null) 
     { 
      // some results from reflection 
      string word = "Hello"; 
      string quote = "'"; 

      formattingLogic = dt => 
      { 
       StringBuilder str = new StringBuilder(quote); 
       str.Append(formatting(dt)); 

       if (!string.IsNullOrWhiteSpace(word)) 
        str.Append(" ").Append(word); 

       str.Append(quote); 
       return str.ToString(); 
      }; 
     } 

     return formattingLogic; 
    } 
} 

void Main() 
{ 
    Console.WriteLine(FormattingLogic(new DateTime(2013, 02, 01))); // '01.02.2013 Hello' 
    Console.WriteLine(FormattingLogic(new DateTime(2013, 11, 26))); // '26.11.2013 Hello' 
} 

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

अब निश्चित रूप से आप इसे अभिव्यक्ति के रूप में भी बना सकते हैं और संकलित फ़ंक्शन को स्टोर कर सकते हैं, लेकिन मैं तर्क दूंगा कि ऐसा करने से यह बहुत अधिक पठनीय और रखरखाव योग्य है।

+0

लैम्बडा को 'Expression.Block()' जैसी चीज़ों का उपयोग करके .Net 4.0 अभिव्यक्ति वृक्ष के रूप में व्यक्त किया जा सकता है। हालांकि आप सी # लैम्ब्डा का उपयोग करके सीधे उस अभिव्यक्ति को बनाने में सक्षम नहीं होंगे। – svick

+0

@ एसविक हाँ, यही मेरा मतलब था। उस हिस्से को अब मेरे उत्तर से बाहर ले लिया क्योंकि यह वास्तव में प्रासंगिक नहीं है :) – poke

+1

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

1

आप उपयोग कर सकते हैं:

Expression<Func<DateTime, string>> formattingExpression = 
    dt => dt.ToShortDateString(); 

और परिणाम:

var result = requiredLambda 
    .Invoke(formattingExpression.Compile(), new DateTime(2003, 2, 1)); 

// 1.2.2003 Hello! 
+1

उत्तर के लिए धन्यवाद। आदर्श रूप में मैं 'आवश्यक लैम्बडा' के प्रकार को 'Func ' के रूप में रखना चाहता हूं, लेकिन अगर आपका उत्तर इसके आसपास कोई रास्ता नहीं मिल रहा है तो आपका उत्तर निश्चित रूप से उपयोगी है। – Lawrence

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