.NET Framework
अभिव्यक्ति पेड़
खोज…
टिप्पणियों
एक्सप्रेशन ट्री .NET फ्रेमवर्क में कोड एक्सप्रेशन को दर्शाने के लिए उपयोग की जाने वाली डेटा संरचनाएँ हैं। उन्हें कोड द्वारा उत्पन्न किया जा सकता है और कोड को किसी अन्य भाषा में अनुवाद करने या इसे निष्पादित करने के लिए प्रोग्रामेटिक रूप से पता लगाया जा सकता है। अभिव्यक्ति पेड़ का सबसे लोकप्रिय जनरेटर सी # कंपाइलर ही है। C # कंपाइलर अभिव्यक्ति के पेड़ उत्पन्न कर सकता है यदि एक लैम्ब्डा अभिव्यक्ति को टाइप एक्सप्रेशन <Func <... >> के एक चर को सौंपा गया है। आमतौर पर यह LINQ के संदर्भ में होता है। सबसे लोकप्रिय उपभोक्ता एंटिटी फ्रेमवर्क का LINQ प्रदाता है। यह एंटिटी फ्रेमवर्क को दिए गए एक्सप्रेशन ट्री का उपभोग करता है और समकक्ष SQL कोड बनाता है जिसे तब डेटाबेस के विरुद्ध निष्पादित किया जाता है।
सरल अभिव्यक्ति ट्री सी # संकलक द्वारा उत्पन्न
निम्नलिखित सी # कोड पर विचार करें
Expression<Func<int, int>> expression = a => a + 1;
क्योंकि C # कंपाइलर देखता है कि लैम्ब्डा एक्सप्रेशन एक डेलिगेट टाइप के बजाय एक्सप्रेशन टाइप को सौंपा गया है, जो इस कोड के बराबर एक एक्सप्रेशन ट्री बनाता है।
ParameterExpression parameterA = Expression.Parameter(typeof(int), "a");
var expression = (Expression<Func<int, int>>)Expression.Lambda(
Expression.Add(
parameterA,
Expression.Constant(1)),
parameterA);
पेड़ की जड़ लंबोदर अभिव्यक्ति है जिसमें एक शरीर और मापदंडों की एक सूची है। लैम्ब्डा में 1 पैरामीटर है जिसे "ए" कहा जाता है। शरीर सीएलआर टाइप बाइनरीएक्सप्रेशन और ऐड के नोडोडाइप की एकल अभिव्यक्ति है। यह अभिव्यक्ति जोड़ को दर्शाता है। इसके दो उप-खंड हैं जिन्हें वाम और अधिकार के रूप में निरूपित किया गया है। बाएँ पैरामीटर "a" के लिए ParameterExpression है और मान 1 के साथ एक ConstantExpression है।
इस अभिव्यक्ति का सबसे सरल उपयोग इसे प्रिंट कर रहा है:
Console.WriteLine(expression); //prints a => (a + 1)
जो बराबर C # कोड प्रिंट करता है।
अभिव्यक्ति ट्री को C # प्रतिनिधि द्वारा CLR में संकलित किया जा सकता है
Func<int, int> lambda = expression.Compile();
Console.WriteLine(lambda(2)); //prints 3
आमतौर पर अभिव्यक्तियों को एसक्यूएल जैसी अन्य भाषाओं में अनुवादित किया जाता है, लेकिन इसका उपयोग प्रतिबिंबन के विकल्प के रूप में निजी या संरक्षित और सार्वजनिक या गैर-सार्वजनिक प्रकार के आंतरिक सदस्यों को आमंत्रित करने के लिए भी किया जा सकता है।
प्रपत्र फ़ील्ड == मान का एक विधेय बनाना
रनटाइम में _ => _.Field == "VALUE"
जैसी अभिव्यक्ति बनाने के लिए।
एक विधेय _ => _.Field
और एक स्ट्रिंग मान "VALUE"
को देखते हुए, एक अभिव्यक्ति बनाएं जो यह परीक्षण करता है कि विधेय सत्य है या नहीं।
अभिव्यक्ति इसके लिए उपयुक्त है:
- विधेय का परीक्षण करने के लिए
IQueryable<T>
,IEnumerable<T>
। - इकाई ढांचा या
Linq
SQL
करने के लिएWhere
एक विधेय परीक्षण है कि विधेय बनाने के लिएSQL
।
यह विधि एक उपयुक्त Equal
अभिव्यक्ति का निर्माण करेगी जो परीक्षण करती है कि Field
"VALUE"
बराबर है या नहीं।
public static Expression<Func<T, bool>> BuildEqualPredicate<T>(
Expression<Func<T, string>> memberAccessor,
string term)
{
var toString = Expression.Convert(Expression.Constant(term), typeof(string));
Expression expression = Expression.Equal(memberAccessor.Body, toString);
var predicate = Expression.Lambda<Func<T, bool>>(
expression,
memberAccessor.Parameters);
return predicate;
}
Where
एक विस्तार विधि में विधेय को सम्मिलित करके विधेय का उपयोग किया जा सकता है।
var predicate = PredicateExtensions.BuildEqualPredicate<Entity>(
_ => _.Field,
"VALUE");
var results = context.Entity.Where(predicate).ToList();
एक स्थिर क्षेत्र को पुनः प्राप्त करने के लिए अभिव्यक्ति
इस तरह के उदाहरण होने के बाद:
public TestClass
{
public static string StaticPublicField = "StaticPublicFieldValue";
}
हम StaticPublicField का मान पुनः प्राप्त कर सकते हैं:
var fieldExpr = Expression.Field(null, typeof(TestClass), "StaticPublicField");
var labmda = Expression.Lambda<Func<string>>(fieldExpr);
यह तब हो सकता है अर्थात फ़ील्ड मान प्राप्त करने के लिए एक प्रतिनिधि में संकलित किया गया।
Func<string> retriever = lambda.Compile();
var fieldValue = retriever();
// fieldValue परिणाम StaticPublicFieldValue है
इनवोकेशन एक्सप्रेशन क्लास
InvocationExpression वर्ग अन्य लैम्ब्डा अभिव्यक्तियों के आह्वान की अनुमति देता है जो एक ही अभिव्यक्ति वृक्ष के हिस्से हैं।
आप उन्हें स्टैटिक Expression.Invoke
विधि से बनाते हैं।
समस्या हम उन वस्तुओं पर प्राप्त करना चाहते हैं जिनके विवरण में "कार" है। हमें अंदर एक स्ट्रिंग की खोज करने से पहले अशक्त के लिए इसे जांचने की आवश्यकता है लेकिन हम नहीं चाहते हैं कि इसे अत्यधिक कहा जाए, क्योंकि गणना महंगी हो सकती है।
using System;
using System.Linq;
using System.Linq.Expressions;
public class Program
{
public static void Main()
{
var elements = new[] {
new Element { Description = "car" },
new Element { Description = "cargo" },
new Element { Description = "wheel" },
new Element { Description = null },
new Element { Description = "Madagascar" },
};
var elementIsInterestingExpression = CreateSearchPredicate(
searchTerm: "car",
whereToSearch: (Element e) => e.Description);
Console.WriteLine(elementIsInterestingExpression.ToString());
var elementIsInteresting = elementIsInterestingExpression.Compile();
var interestingElements = elements.Where(elementIsInteresting);
foreach (var e in interestingElements)
{
Console.WriteLine(e.Description);
}
var countExpensiveComputations = 0;
Action incCount = () => countExpensiveComputations++;
elements
.Where(
CreateSearchPredicate(
"car",
(Element e) => ExpensivelyComputed(
e, incCount
)
).Compile()
)
.Count();
Console.WriteLine("Property extractor is called {0} times.", countExpensiveComputations);
}
private class Element
{
public string Description { get; set; }
}
private static string ExpensivelyComputed(Element source, Action count)
{
count();
return source.Description;
}
private static Expression<Func<T, bool>> CreateSearchPredicate<T>(
string searchTerm,
Expression<Func<T, string>> whereToSearch)
{
var extracted = Expression.Parameter(typeof(string), "extracted");
Expression<Func<string, bool>> coalesceNullCheckWithSearch =
Expression.Lambda<Func<string, bool>>(
Expression.AndAlso(
Expression.Not(
Expression.Call(typeof(string), "IsNullOrEmpty", null, extracted)
),
Expression.Call(extracted, "Contains", null, Expression.Constant(searchTerm))
),
extracted);
var elementParameter = Expression.Parameter(typeof(T), "element");
return Expression.Lambda<Func<T, bool>>(
Expression.Invoke(
coalesceNullCheckWithSearch,
Expression.Invoke(whereToSearch, elementParameter)
),
elementParameter
);
}
}
उत्पादन
element => Invoke(extracted => (Not(IsNullOrEmpty(extracted)) AndAlso extracted.Contains("car")), Invoke(e => e.Description, element))
car
cargo
Madagascar
Predicate is called 5 times.
पहली बात यह है कि वास्तविक उचित पहुंच कैसे है, एक इनवोक में लिपटे:
Invoke(e => e.Description, element)
, और यह एकमात्र हिस्सा है जो e.Description
छूता है, और इसके स्थान पर, टाइप string
extracted
पैरामीटर को अगले एक पर दिया जाता है:
(Not(IsNullOrEmpty(extracted)) AndAlso extracted.Contains("car"))
यहां एक और महत्वपूर्ण बात ध्यान देने योग्य है, वह है AndAlso
। यह केवल बाएं हिस्से की गणना करता है, यदि पहला भाग 'गलत' लौटाता है। इसके बजाय बिटवाइज़ ऑपरेटर 'और' का उपयोग करना एक सामान्य गलती है, जो हमेशा दोनों भागों की गणना करता है, और इस उदाहरण में NullReferenceException के साथ विफल हो जाएगा।