Äúðâåòà îò èçðàçè (expression trees)
Êàêòî âèäÿõìå â ïðåäèøíàòà òî÷êà, ëàìáäà
èçðàçèòå ñà ðàçøèðåíèå íà àíîíèìíèòå ìåòîäè. À àíîíèìíèòå ìåòîäè, êàòî âñåêè
ìåòîä, ìîãàò äà ñå ñâåäàò äî ïðîìåíëèâà ÷ðåç èçïîëçâàíå íà äåëåãàòè.  òîçè ðåä
íà ìèñëè, àêî èìàìå ïðîìåíëèâà, êîÿòî ïðåäñòàâëÿâà ìåòîä, òî ñúîòâåòíî ìîæåì äà
èìàìå è ìàñèâè îò ìåòîäè. À çàùî íå è êîëåêöèè îò ìåòîäè (äåëåãàòè)!?
Ïî òîçè íà÷èí ìîæå äà ñå çàäàäå ïúò íà
îáðàáîòêà íà îïðåäåëåíè äàííè ïî âðåìå íà èçïúëíåíèå íà ïðîãðàìàòà. À ñëåä êàòî
èìàìå ìàñèâè è êîëåêöèè îò ìåòîäè, çàùî äà íå ìîæå äà ñå ïîñòðîè äúðâî îò
ìåòîäè!? Âñúùíîñò òî÷íî òîâà ïðåäñòâàâëÿâàò äúðâåòàòà îò èçðàçè: ïîçíàòîòî
äúðâî, êàòî ñòðóêòóðà îò äàííè, íî âìåñòî äàííè ðàçïîëàãàìå ñ ìåòîäè.
Êàêòî ïîâå÷åòî îò âàñ âå÷å ñèãóðíî ñå äîñåùàò
ñëåä êàòî ñòèãíàõìå äî äúðâåòàòà, âúðõó òÿõ ìîæå äà ïðèëîæèì âñè÷êè àëãîðèòìè,
îïòèìèçàöèè, îáõîæäàíèÿ è äð., êîèòî ñúùåñòâóâàò â òåîðèÿòà è ïðàêòèêàòà. (
Òîâà å äîáðà ïðè÷èíà äà èçòúðñêàìå ïðàõà îò îñíîâíèòå àëãîðèòìè çà ðàáîòà â äúðâåòà,
ñ êîèòî íè çàíèìàâàõà â óíèâåðñèòåòà J )
LINQ ïðåäëàãà êëàñ, ñ êîéòî ìåòîäèòå âå÷å ñå
òðåòèðàò êàòî äàííè îò êîìïèëàòîðà. Íàïðèìåð àêî íàïèøåì ñëåäíèÿò êîä:
public delegate long Operation(long first, int second);
private void Test ()
{
Operation op = (x,y)
=> x+y;
long res =
op.Invoke(2,5);
}
|
Òî êîìïèëàòîðúò ùå
ãåíåðèðà:
public delegate long Operation(long first, int second);
private void Test()
{
Operation op = delegate (long x, int y) {
return x + y;
};
long res = op((long) 2, 5);
}
|
Òîâà èçãëåæäà ñàìî
êàòî ñèíòàêòè÷íî ïîäîáðåíèå. Àêî îáà÷å èçïîëçâàìå êëàñúò Expression<T> (System.Expressions.Expression<T>
îò àñåìáëèòî System.Query.dll), çà äà îáâèåì ìåòîäà è íàïèøåì:
static Expression<Func<int, int, long>>
sumExpr = ( x, y) => x + y;
|
Òî ñëåä êîìïèëàöèÿ
ùå èìàìå:
private static Expression<Func<int, int, long>>
sumExpr;
static Form1()
{
ParameterExpression x;
ParameterExpression y;
sumExpr
= Expression.Lambda<Func<int, int, long>>(
Expression.Convert(
Expression.Add(
x = Expression.Parameter(typeof(int), "x"),
y = Expression.Parameter(typeof(int), "y")),
typeof(long)),
new ParameterExpression[] { x, y
});
}
|
Ìàëêî ïî-ðàçëè÷íî îò ãîðíèÿò ïðèìåð, íàëè!?
Íåêà äà îáÿñíèì êàêâî íàïèñàõìå, òà êîìïèëàòîðà ãî ðàçáðà ïî òàêúâ íà÷èí.
Íåêà äà çàïî÷íåì ñ òèïà, êîéòî ïîäàõìå íà Expression<T>: Func<int, int, long>. Ñëåä êàòî èìàìå ñòðóêòóðè îò äàííè, êîèòî âñúùíîñò ñúäúðæàò ìåòîäè,
òî òðÿáâà ïî íÿêàêúâ íà÷èí äà ðàçáèðàìå ìåòîäèòå, êîèòî èìàìå – òðÿáâà äà çíàåì
áðîÿ íà ïàðàìåòðèòå, êîèòî ñå ïðèåìàò, òåõíèÿò òèï, è òèïà íà âðúùàíèÿò
ðåçóëòàò.
Çà öåëèòå íà LINQ
â àñåìáëèòî System.Query.dll ( ïðîñòðàíñòâîòî îò èìåíà System.Query) èìà äåôèíèðàíè ñëåäíèòå äåëåãàòè:
public delegate T Func<T>()
public delegate T Func<A0,
T>(A0 arg0)
public delegate T Func<A0, A1,
T>(A0 arg0, A1 arg1)
public delegate T Func<A0, A1,
A2, T>(A0 arg0, A1 arg1, A2 arg2)
public delegate T Func<A0, A1,
A2, A3, T>(A0 arg0, A1 arg1, A2 arg2, A3 arg3)
|
Êàêòî çàáåëÿçâàòå âúâ òðèúãúëíèòå ñêîáè ñà
òèïîâåòå íà ïàðàìåòðèòå (A0, A1,…), ïîñëåäíèÿò å òèïà íà âðúùàíèÿò ðåçóëòàò. Èçãëåæäà òîâà ñà äåôèíèöèèòå
íà íàé-èçïîëçâàíèòå ìåòîäè, íî òîâà íå îçíà÷àâà, ÷å íå ìîæå äà ïîäàäåòå íà Expression<T> äåëåãàò îò âàø òèï.
Íåêà äà ðàçãëåäàìå êàêâî ñå ñëó÷âà â
ñòàòè÷íèÿò êîíñòðóêòîð ( òîâà å ãåíåðèðàí îò êîìïèëàòîðà êîä ):
·
Íà ïúðâèòå äâà ðåäà ñå äåêëàðèðàò ïàðàìåòðèòå (x è y) îò òèï ParameterExpression.
·
Íà ñëåäâàùèÿò ðåä ñå çàäàâà èçðàç îò òèï Expression.Lambda<Func<int, int, long>>. Çà äà ðàçáåðåì ñúùíîñòòà íà òîâà äåéñòâèå, ùå ãî ðàçãëåäàìå îò âúòðå
íàâúí:
o
Äîáàâÿ ñå íîâ èçðàç îò òèï BinaryExpression ÷ðåç Expression.Add, êàòî ñå çàäàâàò ïàðàìåòðèòå
è òåõíèÿò òèï.
o
Ñëåä òîâà ñ ïîìîùòà íà ìåòîäà Expression.Convert expr1 ñå êîíâåðòèðà äî îáåêò îò òèï Expression êàòî ñå çàäàâà è
òèï íà ðåçóëòàòà.
o
È íàêðàÿ ÷ðåç Expression.Lambda<Func<int, int, long>> ñå ñúçäàâà îáåêò îò òèï LambdaExpression, êàòî ñå ïîäàâàò
èíñòàíöèèòå íà ñúçäàäåíèòå ïàðàìåòðè.
Çà äà å ïî-ëåñíî çà
÷åòåíå ñúùèÿò êîä ìîæå äà ñå ïðåñòàâè è òàêà:
ParameterExpression x;
ParameterExpression y;
BinaryExpression expr1 = Expression.Add(
x = Expression.Parameter(typeof(int), "x"),
y = Expression.Parameter(typeof(int), "y"));
Expression expr2 = Expression.Convert(expr1,typeof(long));
sumExpr1 = Expression.Lambda<Func<int, int, long>>(
expr2, new ParameterExpression[] { x, y
});
|
È íàêðàÿ èçðàçèòå
ìîãàò äà ñå èçïîëçâàò ïî ñëåäíèÿò íà÷èí:
Func<int, int, long> func =
sumExpr.Compile();
long res =
func(3,5);
|
Êàêòî, ìîæå áè, ñå äîñåùàòå äúðâåòàòà îò
èçðàçè ìîãàò äà ñå èçïîëçâàò çà äèíàìè÷íî ãåíåðèðàíå íà èçïúëíèì êîä.
 òàçè òî÷êà íå ðàçãëåäàõìå êîíêðåòíè
ïðàêòè÷åñêè ïðèìåðè, íî äúðâåòàòà îò èçðàçè ñà âàæíà ÷àñò îò LINQ è C# 3.0 è áåç òÿõ ãîëÿìà ÷àñò îò íîâîâúâåäåíèÿòà
íå áèõà áèëè âúçìîæíè. Ïðè ðàáîòà ñ ãîëÿìà ÷àñò îò ðàçøèðÿâàùèòå ìåòîäè è LINQ
to SQL, LINQ to Object è äð. ìîæå äà çàáåëåæèì, ÷å çàÿâêèòå ñå
ïðåóáðàçóâàò îò äúðâåòà îò èçðàçè, êîèòî ñå èçïúëíÿâàò â ìîìåíòà íà èçâèêâàíå.
Ñëåäâàùà ÷àñò: LINQ â äåòàéëè