2014-11-18 9 views
5

में एक अभिव्यक्ति का उपयोग करते हुए मैं जानता हूँ कि मैं का उपयोग कर अभिव्यक्ति पेड़ बना सकते हैं:एक संकलक-उत्पन्न अभिव्यक्ति पेड़

  1. फैक्टरी तरीकों।

  2. Expression पर लैम्ब्डा अभिव्यक्ति का कंपाइलर रूपांतरण।

जटिल अभिव्यक्ति पेड़ के लिए मैं 2 पसंद करूंगा क्योंकि यह अधिक संक्षिप्त है।

क्या इस तरह से पहले से निर्मित Expressions का उपयोग करना संभव है?

using System; 
using System.Linq.Expressions; 

public class Test 
{ 
    public static Expression<Func<int, int>> Add(Expression expr) 
    { 
#if false 
     // works 
     ParameterExpression i = Expression.Parameter(typeof(int)); 
     return Expression.Lambda<Func<int, int>>(Expression.Add(i, expr), i); 
#else 
     // compiler error, can I pass expr here somehow? 
     return i => i + expr; 
#endif 
    } 

    public static void Main() 
    { 
     Func<int, int> f = Add(Expression.Constant(42)).Compile(); 
     Console.WriteLine(f(1)); 
    } 
} 
+0

मुझे लगता है कि आप सभी की जरूरत अभिव्यक्तिकरने के लिए अभिव्यक्ति से ऐड में अभिव्यक्ति का प्रकार निर्दिष्ट करने के लिए हैक्योंकि मुझे लगता है कि उपरोक्त उदाहरण में क्या हो रहा है लेकिन यह अनुमानित है। –

उत्तर

2

आप संकलन समय अभिव्यक्ति के पेड़ के साथ मनमाना Expression उदाहरणों मिश्रण नहीं कर सकते। आप कर सकते हैं विशिष्ट नोड्स के साथ एक नया अभिव्यक्ति वृक्ष बनाते हैं, इसलिए आपके पास i => i + marker हो सकता है और फिर नोड के साथ एक नया पेड़ बनाएं जो आपके रनटाइम अभिव्यक्ति द्वारा प्रतिस्थापित किया गया हो। यह लेखन की आवश्यकता है एक उचित ExpressionVisitor:

public static class ExpressionExtensions { 
    public static T AsPlaceholder<T>(this Expression expression) { 
    throw new InvalidOperationException(
     "Expression contains placeholders." 
    ); 
    } 

    public static Expression FillPlaceholders(this Expression expression) { 
    return new PlaceholderExpressionVisitor().Visit(expression); 
    } 
} 

class PlaceholderExpressionVisitor : ExpressionVisitor { 
    protected override Expression VisitMethodCall(MethodCallExpression node) { 
    if (
     node.Method.DeclaringType == typeof(ExpressionExtensions) && 
     node.Method.Name == "AsPlaceholder" // in C# 6, we would use nameof() 
    ) { 
     return Expression.Lambda<Func<Expression>>(node.Arguments[0]).Compile()(); 
    } else { 
     return base.VisitMethodCall(node); 
    } 
    } 
} 

Add अब हो जाता है:

public static Expression<Func<int, int>> Add(Expression expr) { 
    Expression<Func<int, int>> add = i => i + expr.AsPlaceholder<int>(); 
    return (Expression<Func<int, int>>) add.FillPlaceholders(); 
} 

थोड़ा गुप्त अभिव्यक्ति

Expression.Lambda<Func<Expression>>(node.Arguments[0]).Compile()() 

को देख कि संकलक अभिव्यक्ति हम पर कब्जा द्वारा समझाया जा सकता एक बंद होने में फिर से डालने के बावजूद, यह कहां से आया था, इसलिए विधि कॉल का तर्क हमेशा इस बंद होने का संदर्भ है कि हम वास्तविक अभिव्यक्ति प्राप्त करने के लिए मूल्यांकन करने के लिए ed।

यह स्पष्ट रूप से स्पष्ट टाइपिंग के साथ प्रतिस्थापन अभिव्यक्तियों की मनमानी संख्या के लिए स्केल करता है।

+0

मैंने इस उत्तर को Servy's 'ReplaceVisitor' का उपयोग एक उपन्यास दृष्टिकोण के उपयोग के विभिन्न तरीकों से जाने के लिए संशोधित किया है जहां हम अभिव्यक्तियों को स्वयं प्लेसहोल्डर्स के रूप में उपयोग करते हैं। –

7

बॉक्स से कुछ भी नहीं है, लेकिन आप इस कार्यक्षमता को प्रदान करने के लिए स्वयं एक टूल बना सकते हैं।

आप एक ऐसी विधि लिख सकते हैं जो दो पैरामीटर, एक "वास्तविक" पैरामीटर और कुछ मान का एक पैरामीटर स्वीकार करता है जिसे आप किसी अन्य अभिव्यक्ति के मूल्य के साथ प्रतिस्थापित करना चाहते हैं। इसके बाद आप एक अभिव्यक्ति है कि मूल्य पर ले कर जाता है और दूसरा अभिव्यक्ति के साथ पैरामीटर के सभी उदाहरणों की जगह ले सकता:

public static Expression<Func<TSource, TResult>> BuildExpression 
    <TSource, TOther, TResult>(
    Expression<Func<TSource, TOther, TResult>> function, 
    Expression<Func<TOther>> innerExpression) 
{ 
    var body = function.Body.Replace(function.Parameters[1], innerExpression.Body); 
    return Expression.Lambda<Func<TSource, TResult>>(body, function.Parameters[0]); 
} 

आप किसी अन्य के साथ एक अभिव्यक्ति के सभी उदाहरणों को बदलने के लिए निम्न विधि का उपयोग कर सकते हैं:

public static Expression Replace(this Expression expression, 
    Expression searchEx, Expression replaceEx) 
{ 
    return new ReplaceVisitor(searchEx, replaceEx).Visit(expression); 
} 
internal class ReplaceVisitor : ExpressionVisitor 
{ 
    private readonly Expression from, to; 
    public ReplaceVisitor(Expression from, Expression to) 
    { 
     this.from = from; 
     this.to = to; 
    } 
    public override Expression Visit(Expression node) 
    { 
     return node == from ? to : base.Visit(node); 
    } 
} 

फिर आप यह आपके मामले के लिए आवेदन कर सकते इस प्रकार है:

public static Expression<Func<int, int>> Add(Expression<Func<int>> expr) 
{ 
    return BuildExpression((int i, int n) => i + n, expr); 
} 
+0

अच्छा।किसी कारण से मुझे यह ध्यान में था कि आपको 'अभिव्यक्तिविज्ञानी' के सभी सदस्यों को ओवरराइड करने की ज़रूरत है, लेकिन निश्चित रूप से बस 'विज़िट() 'करेगा। मुझे यकीन है कि 'BuildExpression' के लिए कुछ और सामान्य बनाया जा सकता है, हालांकि - यह स्पष्ट नहीं है कि यह अभिव्यक्ति के साथ दूसरे पैरामीटर को प्रतिस्थापित कर रहा है, और इसे एकाधिक प्रतिस्थापित मूल्यों में स्केल करने के साथ ही दर्द भी होगा। –

+0

@JeroenMostert इसे एक बड़ी डिग्री के लिए जेनरेट करना निश्चित रूप से संभव है, लेकिन कुछ हद तक अधिक काम, हां, इसलिए सादगी के लिए मैंने इस समस्या को हल करने के लिए केवल इतना ही सीमित किया है कि यह समझ में आता है। – Servy

+0

मैंने 'ReplaceVisitor' का उपयोग करने के संभावित वैकल्पिक तरीके से अपना उत्तर अपडेट किया। मुझे लगता है कि मैं अभ्यास के रूप में थोड़ी देर के लिए पवित्र Grail की तलाश में रहना होगा। :-) –

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