Galin Iliev

Software Architecture & Development

Äúðâåòà îò èçðàçè (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 â äåòàéëè

Content