खोज…


परिचय

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

वाक्य - विन्यास

  • अभिव्यक्ति <TDelegate> नाम = lambdaExpression;

पैरामीटर

पैरामीटर विवरण
TDelegate अभिव्यक्ति के लिए इस्तेमाल किया जाने वाला प्रतिनिधि प्रकार
lambdaExpression लैम्ब्डा एक्सप्रेशन (उदा। num => num < 5 )

टिप्पणियों

अभिव्यक्ति पेड़ों का परिचय

हम कहां से आए

अभिव्यक्ति के पेड़ रनटाइम में "स्रोत कोड" का उपभोग करने के बारे में हैं। एक विधि पर विचार करें जो बिक्री आदेश decimal CalculateTotalTaxDue(SalesOrder order) कारण बिक्री कर की गणना करता है। .NET प्रोग्राम में उस विधि का उपयोग करना आसान है - आप इसे decimal taxDue = CalculateTotalTaxDue(order); । क्या होगा यदि आप इसे दूरस्थ क्वेरी (SQL, XML, एक दूरस्थ सर्वर, आदि) से सभी परिणामों पर लागू करना चाहते हैं? उन दूरस्थ क्वेरी स्रोत विधि को कॉल नहीं कर सकते हैं! परंपरागत रूप से, आपको इन सभी मामलों में प्रवाह को उलटना होगा। संपूर्ण क्वेरी बनाएं, इसे मेमोरी में स्टोर करें, फिर परिणामों के माध्यम से लूप करें और प्रत्येक परिणाम के लिए कर की गणना करें।

प्रवाह उलटा स्मृति और विलंबता समस्याओं से कैसे बचें

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

यहाँ समस्या यह है कि एक दूरस्थ क्वेरी हमारे तरीके तक नहीं पहुँच सकती है । हम इस समस्या से बच सकते हैं यदि इसके बजाय, हमने विधि के निर्देश दूरस्थ क्वेरी को भेजे। हमारे CalculateTotalTaxDue उदाहरण में, इसका मतलब है कि हम यह जानकारी भेजते हैं:

  1. कुल कर संग्रह करने के लिए एक चर बनाएं
  2. आदेश पर सभी लाइनों के माध्यम से लूप
  3. प्रत्येक पंक्ति के लिए, जांचें कि उत्पाद कर योग्य है या नहीं
  4. यदि यह है, तो लागू कर की दर से कुल लाइन को गुणा करें और उस राशि को कुल में जोड़ें
  5. नहीं तो कुछ मत करो

उन निर्देशों के साथ, यह डेटा बनाते समय दूरस्थ क्वेरी काम कर सकती है।

इसे लागू करने के लिए दो चुनौतियां हैं। आप संकलित .NET विधि को निर्देशों की सूची में कैसे परिवर्तित करते हैं, और आप निर्देशों को इस तरह से कैसे प्रारूपित करते हैं कि वे रिमोट सिस्टम द्वारा उपभोग किए जा सकते हैं?

अभिव्यक्ति के पेड़ के बिना, आप केवल MSIL के साथ पहली समस्या को हल कर सकते हैं। (MSIL .NET कंपाइलर द्वारा बनाया गया कोडांतरक जैसा कोड है।) MSIL को पार्स करना संभव है , लेकिन यह आसान नहीं है। यहां तक कि जब आप इसे ठीक से करते हैं, तो यह निर्धारित करना कठिन हो सकता है कि मूल प्रोग्रामर का इरादा किसी विशेष दिनचर्या के साथ क्या था।

अभिव्यक्ति के पेड़ दिन बचाते हैं

अभिव्यक्ति के पेड़ इन सटीक मुद्दों को संबोधित करते हैं। वे प्रोग्राम निर्देशों का प्रतिनिधित्व करते हैं एक पेड़ डेटा संरचना जहां प्रत्येक नोड एक निर्देश का प्रतिनिधित्व करता है और उस अनुदेश को निष्पादित करने के लिए आवश्यक सभी जानकारी का संदर्भ देता है। उदाहरण के लिए, MethodCallExpression में 1 का संदर्भ है) MethodInfo इसे कॉल करने वाला है, 2) Expression s की एक सूची यह उस विधि से पास होगी, 3) उदाहरण के तरीकों के लिए, जिस Expression आप विधि को कॉल करेंगे। आप "पेड़ पर चल सकते हैं" और अपने दूरस्थ क्वेरी पर निर्देश लागू कर सकते हैं।

अभिव्यक्ति के पेड़ बनाना

एक अभिव्यक्ति पेड़ बनाने का सबसे आसान तरीका एक लंबोदर अभिव्यक्ति के साथ है। ये अभिव्यक्तियाँ सामान्य C # विधियों के समान ही दिखाई देती हैं। यह संकलक जादू है एहसास करने के लिए महत्वपूर्ण है। जब आप पहली बार एक लैम्ब्डा एक्सप्रेशन बनाते हैं, तो कंपाइलर चेक करता है कि आप उसे क्या असाइन करते हैं। यदि यह एक Delegate प्रकार ( Action या Func सहित) है, तो कंपाइलर लैम्बडा एक्सप्रेशन को एक प्रतिनिधि में बदल देता है। यदि यह एक LambdaExpression (या Expression<Action<T>> या Expression<Func<T>> जो दृढ़ता से टाइप किया गया है LambdaExpression ), तो कंपाइलर इसे LambdaExpression में बदल LambdaExpression । यह वह जगह है जहाँ जादू अंदर रहता है। दृश्यों के पीछे, कंपाइलर ने अपने ट्री LambdaExpression एक्सप्रेशन को LambdaExpression एक्सप्रेशन में बदलने के लिए एक्सप्रेशन ट्री एपीआई का उपयोग किया

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

नोट: यहां नाम थोड़ा भ्रमित करते हैं। एक लैम्ब्डा एक्सप्रेशन (दो शब्द, लोअर केस) एक => इंडिकेटर के साथ कोड के ब्लॉक को संदर्भित करता है। यह C # में एक अनाम विधि का प्रतिनिधित्व करता है और इसे एक Delegate या Expression में परिवर्तित किया जाता है। एक LambdaExpression (एक शब्द, PascalCase) अभिव्यक्ति एपीआई के भीतर नोड प्रकार को संदर्भित करता है जो एक ऐसी विधि का प्रतिनिधित्व करता है जिसे आप निष्पादित कर सकते हैं।

अभिव्यक्ति पेड़ और LINQ

अभिव्यक्ति पेड़ों का सबसे आम उपयोग LINQ और डेटाबेस प्रश्नों के साथ है। LINQ एक प्रमोशन ट्री को एक क्वेरी प्रोवाइडर के साथ जोड़कर आपके निर्देशों को लक्ष्य दूरस्थ क्वेरी पर लागू करता है। उदाहरण के लिए, LINQ to Entity फ्रेमवर्क क्वेरी प्रोवाइडर एक अभिव्यक्ति ट्री को SQL में बदल देता है जिसे सीधे डेटाबेस के खिलाफ निष्पादित किया जाता है।

सभी टुकड़ों को एक साथ रखकर, आप LINQ के पीछे असली शक्ति देख सकते हैं।

  1. लैम्ब्डा एक्सप्रेशन का उपयोग करते हुए एक प्रश्न लिखें: products.Where(x => x.Cost > 5)
  2. कंपाइलर उस एक्सप्रेशन ट्री को निर्देशों के साथ एक अभिव्यक्ति ट्री में बदल देता है "जाँच करें कि क्या पैरामीटर की लागत संपत्ति पांच से अधिक है"।
  3. क्वेरी प्रदाता अभिव्यक्ति ट्री को पार्स करता है और एक वैध SQL क्वेरी का SELECT * FROM products WHERE Cost > 5
  4. ORM POCO में सभी परिणामों को प्रोजेक्ट करता है और आपको वापस वस्तुओं की एक सूची मिलती है

टिप्पणियाँ

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

एपीआई का उपयोग करके अभिव्यक्ति पेड़ बनाना

using System.Linq.Expressions;

// Manually build the expression tree for 
// the lambda expression num => num < 5.
ParameterExpression numParam = Expression.Parameter(typeof(int), "num");
ConstantExpression five = Expression.Constant(5, typeof(int));
BinaryExpression numLessThanFive = Expression.LessThan(numParam, five);
Expression<Func<int, bool>> lambda1 =
    Expression.Lambda<Func<int, bool>>(
        numLessThanFive,
        new ParameterExpression[] { numParam });

संकलन पेड़

// Define an expression tree, taking an integer, returning a bool.
Expression<Func<int, bool>> expr = num => num < 5;

// Call the Compile method on the expression tree to return a delegate that can be called.
Func<int, bool> result = expr.Compile();

// Invoke the delegate and write the result to the console.
Console.WriteLine(result(4)); // Prints true

// Prints True.

// You can also combine the compile step with the call/invoke step as below:
Console.WriteLine(expr.Compile()(4));

पार्सिंग अभिव्यक्ति पेड़

using System.Linq.Expressions;

// Create an expression tree.
Expression<Func<int, bool>> exprTree = num => num < 5;

// Decompose the expression tree.
ParameterExpression param = (ParameterExpression)exprTree.Parameters[0];
BinaryExpression operation = (BinaryExpression)exprTree.Body;
ParameterExpression left = (ParameterExpression)operation.Left;
ConstantExpression right = (ConstantExpression)operation.Right;

Console.WriteLine("Decomposed expression: {0} => {1} {2} {3}",
                  param.Name, left.Name, operation.NodeType, right.Value);

// Decomposed expression: num => num LessThan 5      

एक लंबोदर अभिव्यक्ति के साथ अभिव्यक्ति पेड़ बनाएँ

निम्नलिखित सबसे बुनियादी अभिव्यक्ति वृक्ष है जो लंबोदर द्वारा बनाया गया है।

Expression<Func<int, bool>> lambda = num => num == 42;

अभिव्यक्ति के पेड़ 'हाथ से' बनाने के लिए, एक Expression वर्ग का उपयोग करना चाहिए।

उपरोक्त अभिव्यक्ति इसके बराबर होगी:

ParameterExpression parameter = Expression.Parameter(typeof(int), "num"); // num argument
ConstantExpression constant = Expression.Constant(42, typeof(int)); // 42 constant
BinaryExpression equality = Expression.Equals(parameter, constant); // equality of two expressions (num == 42)
Expression<Func<int, bool>> lambda = Expression.Lambda<Func<int, bool>>(equality, parameter);

भावों को समझना एपीआई

हम एक CalculateSalesTax ट्री बनाने के लिए अभिव्यक्ति ट्री एपीआई का उपयोग करने जा रहे हैं। सादे अंग्रेजी में, यहां पेड़ बनाने के लिए उठाए गए कदमों का सारांश है।

  1. जांचें कि क्या उत्पाद कर योग्य है
  2. यदि ऐसा है, तो लागू कर की दर से कुल लाइन को गुणा करें और उस राशि को वापस करें
  3. अन्यथा 0 वापस करें
//For reference, we're using the API to build this lambda expression
    orderLine => orderLine.IsTaxable ? orderLine.Total * orderLine.Order.TaxRate : 0;

//The orderLine parameter we pass in to the method.  We specify it's type (OrderLine) and the name of the parameter.
    ParameterExpression orderLine = Expression.Parameter(typeof(OrderLine), "orderLine");

//Check if the parameter is taxable;  First we need to access the is taxable property, then check if it's true
    PropertyInfo isTaxableAccessor = typeof(OrderLine).GetProperty("IsTaxable");
    MemberExpression getIsTaxable = Expression.MakeMemberAccess(orderLine, isTaxableAccessor);
    UnaryExpression isLineTaxable = Expression.IsTrue(getIsTaxable);

//Before creating the if, we need to create the braches
    //If the line is taxable, we'll return the total times the tax rate; get the total and tax rate, then multiply
    //Get the total
    PropertyInfo totalAccessor = typeof(OrderLine).GetProperty("Total");
    MemberExpression getTotal = Expression.MakeMemberAccess(orderLine, totalAccessor);
    
    //Get the order
    PropertyInfo orderAccessor = typeof(OrderLine).GetProperty("Order");
    MemberExpression getOrder = Expression.MakeMemberAccess(orderLine, orderAccessor);
    
    //Get the tax rate - notice that we pass the getOrder expression directly to the member access
    PropertyInfo taxRateAccessor = typeof(Order).GetProperty("TaxRate");
    MemberExpression getTaxRate = Expression.MakeMemberAccess(getOrder, taxRateAccessor);
    
    //Multiply the two - notice we pass the two operand expressions directly to multiply
    BinaryExpression multiplyTotalByRate = Expression.Multiply(getTotal, getTaxRate);
    
//If the line is not taxable, we'll return a constant value - 0.0 (decimal)
    ConstantExpression zero = Expression.Constant(0M);

//Create the actual if check and branches
    ConditionalExpression ifTaxableTernary = Expression.Condition(isLineTaxable, multiplyTotalByRate, zero);
    
//Wrap the whole thing up in a "method" - a LambdaExpression
    Expression<Func<OrderLine, decimal>> method = Expression.Lambda<Func<OrderLine, decimal>>(ifTaxableTernary, orderLine);

अभिव्यक्ति ट्री बेसिक

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

अभिव्यक्ति पेड़ निष्पादन योग्य कोड के गतिशील संशोधन, विभिन्न डेटाबेस में LINQ प्रश्नों के निष्पादन और गतिशील प्रश्नों के निर्माण में सक्षम बनाता है। आप अभिव्यक्ति पेड़ों द्वारा प्रस्तुत कोड को संकलित और चला सकते हैं।

डायनेमिक लैंग्वेज रन-टाइम (DLR) में डायनेमिक लैंग्वेज और .NET फ्रेमवर्क के बीच इंटरऑपरेबिलिटी प्रदान करने और कंपाइलर राइटर्स को Microsoft इंटरमीडिएट लैंग्वेज (MSIL) के बजाय एक्सप्रेशन ट्री को इनेबल करने के लिए भी इस्तेमाल किया जाता है।

अभिव्यक्ति पेड़ वाया बनाए जा सकते हैं

  1. अनाम लंबोदर अभिव्यक्ति,
  2. मैन्युअल रूप से System.Linq.Expressions नामस्थान का उपयोग करके।

लैम्ब्डा एक्सप्रेशन से अभिव्यक्ति पेड़

जब एक लैम्ब्डा एक्सप्रेशन को एक्सप्रेशन टाइप वेरिएबल को असाइन किया जाता है, तो कंपाइलर एक एक्सप्रेशन ट्री बनाने के लिए कोड उत्सर्जित करता है जो लैम्बडा एक्सप्रेशन को दर्शाता है।

निम्नलिखित कोड उदाहरण दिखाता है कि C # संकलक कैसे एक अभिव्यक्ति ट्री बनाता है जो लैम्ब्डा एक्सप्रेशन num => num <5 का प्रतिनिधित्व करता है।

Expression<Func<int, bool>> lambda = num => num < 5;

एपीआई का उपयोग करके अभिव्यक्ति पेड़

अभिव्यक्ति पेड़ भी अभिव्यक्ति कक्षा का उपयोग कर बनाया। इस वर्ग में स्थिर कारखाने विधियाँ हैं जो विशिष्ट प्रकार के अभिव्यक्ति ट्री नोड बनाते हैं।

नीचे कुछ प्रकार के ट्री नोड्स हैं।

  1. ParameterExpression
  2. MethodCallExpression

निम्नलिखित कोड उदाहरण दिखाता है कि एक अभिव्यक्ति ट्री कैसे बनाया जाता है जो एपीआई का उपयोग करके लैम्बडा अभिव्यक्ति संख्या => संख्या <5 का प्रतिनिधित्व करता है।

ParameterExpression numParam = Expression.Parameter(typeof(int), "num");
ConstantExpression five = Expression.Constant(5, typeof(int));
BinaryExpression numLessThanFive = Expression.LessThan(numParam, five);
Expression<Func<int, bool>> lambda1 = Expression.Lambda<Func<int, bool>>(numLessThanFive,new ParameterExpression[] { numParam });

आगंतुक का उपयोग कर एक अभिव्यक्ति की संरचना की जांच

ExpressionVisitor के कुछ तरीकों को ओवरराइड करके एक नए आगंतुक वर्ग को परिभाषित करें:

class PrintingVisitor : ExpressionVisitor {
    protected override Expression VisitConstant(ConstantExpression node) {
        Console.WriteLine("Constant: {0}", node);
        return base.VisitConstant(node);
    }
    protected override Expression VisitParameter(ParameterExpression node) {
        Console.WriteLine("Parameter: {0}", node);
        return base.VisitParameter(node);
    }
    protected override Expression VisitBinary(BinaryExpression node) {
        Console.WriteLine("Binary with operator {0}", node.NodeType);
        return base.VisitBinary(node);
    }
}

मौजूदा विज़िटर पर इस विज़िटर का उपयोग करने के लिए कॉल Visit :

Expression<Func<int,bool>> isBig = a => a > 1000000;
var visitor = new PrintingVisitor();
visitor.Visit(isBig);


Modified text is an extract of the original Stack Overflow Documentation
के तहत लाइसेंस प्राप्त है CC BY-SA 3.0
से संबद्ध नहीं है Stack Overflow