Expression of trees for dummies?

I am a mannequin in this scenario.

I tried to read on Google what it is, but I just don’t understand. Can someone give me a simple explanation of what they are and why they are useful?

edit: I am talking about the LINQ function in .Net.

+41
c # linq expression-trees
Mar 08 '09 at 11:07
source share
7 answers

An expression tree is a mechanism for converting executable code into data. Using the expression tree, you can create a data structure that represents your program.

In C #, you can work with the expression tree created by lambda expressions using the Expression<T> class.




In a traditional program, you write this code:

 double hypotenuse = Math.Sqrt(a*a + b*b); 

This code causes the compiler to generate an assignment and what it is. In most cases, this is all you need.

Using regular code, your application cannot go back and look at hypotenuse to determine that it was created by making a call to Math.Sqrt() ; this information is simply not part of what is included.

Now consider a lambda expression similar to the following:

 Func<int, int, int> hypotenuse = (a, b) => Math.Sqrt(a*a + b*b); 

This is slightly different from the previous one. Now hypotenuse is actually a reference to a block of executable code. If you call

 hypotenuse(3, 4); 

you will get a return value of 5 .

We can use expression trees to examine the block of executable code that was created. Try instead:

 Expression<Func<int, int, int>> addTwoNumbersExpression = (x, y) => x + y; BinaryExpression body = (BinaryExpression) addTwoNumbersExpression.Body; Console.WriteLine(body); 

This gives:

 (x + y) 

More complex methods and manipulations are possible with expression trees.

+27
Mar 08 '09 at 11:26
source share

The best explanation in expression trees I've ever read is this article by Charlie Calvert.

Summarizing:

The expression tree represents what , not how , which you want to do.

Consider the following very simple lambda expression:
Func<int, int, int> function = (a, b) => a + b;

This operator consists of three sections:

  • Declaration: Func<int, int, int> function
  • Equality Operator: =
  • Lambda expression: (a, b) => a + b;

The function variable points to raw executable code that knows how to add two numbers .

This is the most important difference between delegates and expressions. You call function , not knowing what it will do with the two integers that you passed. It takes two and returns one that your code may know.

In the previous section, you saw how to declare a variable pointing to raw executable code. Expression trees are not executable code ; they are a form of data structure.

Now, unlike delegates, your code may know what the expression tree should do.

LINQ provides a simple syntax for translating code into a data structure called an expression tree. The first step is to add a using statement to represent the Linq.Expressions namespace:

using System.Linq.Expressions;

Now we can create an expression tree:
Expression<Func<int, int, int>> expression = (a, b) => a + b;

The identical lambda expression shown in the previous example is converted to an expression tree declared as type Expression<T> . The expression identifier is not executable code; This is a data structure called an expression tree.

This means that you cannot just invoke the expression tree, just as you could invoke a delegate, but you can parse it. So what can your code understand by parsing the expression variable?

 // `expression.NodeType` returns NodeType.Lambda. // `expression.Type` returns Func<int, int, int>. // `expression.ReturnType` returns Int32. var body = expression.Body; // `body.NodeType` returns ExpressionType.Add. // `body.Type` returns System.Int32. var parameters = expression.Parameters; // `parameters.Count` returns 2. var firstParam = parameters[0]; // `firstParam.Name` returns "a". // `firstParam.Type` returns System.Int32. var secondParam = parameters[1]. // `secondParam.Name` returns "b". // `secondParam.Type` returns System.Int32. 

Here we see that there is a lot of information that we can get from the expression.

But why do we need this?

You have learned that an expression tree is a data structure representing executable code. But so far we have not answered the central question of why such a transformation would have to be done. This is the question we asked at the beginning of this post, and it is time to answer it.

LINQ to SQL query does not execute inside your C # program. Instead, it is converted to SQL, sent by wiring, and executed on the database server. In other words, the following code is never executed inside your program:
var query = from c in db.Customers where c.City == "Nantes" select new { c.City, c.CompanyName };

It is first converted to the following SQL statement, and then executed on the server:
SELECT [t0].[City], [t0].[CompanyName] FROM [dbo].[Customers] AS [t0] WHERE [t0].[City] = @p0

The code found in the query expression must be translated into an SQL query, which can be sent to another process as a string. In this case, this process is the SQL server database. Obviously, it will be much easier to translate a data structure such as an expression tree into SQL than to convert raw IL or executable code to SQL. To exaggerate the complexity of the problem, just imagine that you are trying to translate a series of zeros and ones into SQL!

When it is time to translate the query expression into SQL, the expression tree representing your query will be parsed and parsed in the same way as we divided our simple lambda expression tree in the previous section. Of course, the LINQ to SQL expression tree analysis algorithm is much more complicated than the one we used, but the principle is the same. After he has analyzed parts of the expression tree, LINQ ponders them and decides the best way to write an SQL statement that will return the requested data.

Expression trees were created so that the task of converting code, such as a query expression, into a string that can be passed to another process and executed there. It is so simple. There is no great secret, there is no magic wand to be waved. One simply takes the code, converts it into data, and then analyzes the data to find the components that will be translated into a string that can be transferred to another process.

Since the request comes to the compiler encapsulated in such an abstract data structure, the compiler can freely interpret it in almost any way. He is not forced to fulfill the request in a specific order or in a certain way. Instead, it can parse the expression tree, discover what you want to do, and then decide how to do it. At least theoretically, he has the right to consider any number of factors, such as current network traffic, database load, current results that he has, etc. In practice, LINQ to SQL does not take all these factors into account, but it is theoretically free to do what it wants. In addition, you can transfer this expression tree to some custom code that you write manually that could parse it and translate it into something very different from what LINQ to SQL creates.

And again, we see that expression trees allow us to represent (express?) What we want to do. And we use translators who decide how our expressions are performed.

+11
Dec 24 '13 at 20:00
source share

Expression trees are a representation of an expression in memory, for example. arithmetic or boolean expression. For example, consider an arithmetic expression

 a + b*2 

Since * has a higher operator priority than +, the expression tree is constructed as follows:

  [+] / \ a [*] / \ b 2 

Having this tree, it can be evaluated for any values ​​of a and b. In addition, you can convert it to other expression trees, for example, to get an expression.

When you implement an expression tree, I would suggest creating an Expression base class. From this, the BinaryExpression class will be used for all binary expressions, such as + and *. Then you can enter a VariableReferenceExpression expression to refer to variables (such as a and b) and another class ConstantExpression expression (for example 2).

The expression tree in many cases is constructed as a result of parsing input (from the user directly or from a file). To evaluate the expression tree, I would suggest using a visitor template .

+9
Mar 08 '09 at 11:25
source share

Short answer: It's nice to be able to write the same LINQ query and point it to any data source. Without it, Language Integrated cannot be requested.

Long answer: As you probably know, when you compile the source code, you transform it from one language to another. Usually from high level language (C #) to lower arm (IL).

You can basically do this in two ways:

  • You can translate the code using search and replace
  • You analyze the code and get a parse tree.

The last is what all the programs that we know as "compilers" do.

When you have a parsing tree, you can easily translate it into any other language, and this is what expression trees allow. Since the code is stored as data, you can do whatever you want, but you probably just want to translate it into another language.

Now in LINQ to SQL, expression trees are turned into an SQL command, and then sent to the database server by posting. As far as I know, they do not really do anything when translating the code, but they could. For example, a query provider might generate different SQL code depending on network conditions.

+9
Jun 07 '09 at 8:25
source share

IIUC, an expression tree is similar to an abstract syntax tree, but an expression usually has a single meaning, whereas an AST can represent an entire program (with classes, packages, functions, operators, etc.).

In any case, for the expression (2 + 3) * 5 tree:

  * / \ + 5 / \ 2 3 

Evaluate each node recursively (from bottom to top) to get the value in the root directory of the node, i.e. the value of the expression.

You can have, for example, unary (negative) or trinial (if-then-else) operators, as well as functions (n-ary, i.e. any number of ops), if your expression language allows it.

Type evaluation and type management are performed on similar trees.

+3
Mar 08 '09 at 11:21
source share

DLR
Expression trees are an addition to C # to support the dynamic Runtime Language (DLR). DLR is also responsible for providing us with a "var" method for declaring variables. ( var objA = new Tree(); )

Learn more about DLR .

Essentially, Microsoft wanted to open the CLR for dynamic languages ​​like LISP, SmallTalk, Javascript, etc. To do this, they needed time to parse and evaluate expressions. This was not possible until the advent of DLR.

To my first sentence, expression trees are a complement to C #, which opens up the possibility of using DLR. Before that, C # was a much more static language - all types of variables had to be declared as a specific type, and all code had to be written at compile time.

Using it with data
Expression trees open the flow gate to dynamic code.

Say, for example, that you are creating a real estate site. At the design stage, you know all the filters that you can apply. To implement this code, you have two options: you can write a loop that compares each data point with a series of If-Then checks; or you can try to build a query in dynamic language (SQL) and pass it to a program that can perform a search for you (database).

Using expression trees, you can now change the code in your program - on the fly - and perform a search. In particular, you can do this through LINQ.

(More: MSDN: How to use expression trees to create dynamic queries ).

In addition to data
The main use for expression trees is for data management. However, they can also be used for dynamically generated code. So, if you need a function that is defined dynamically (ala Javascript), you can create an Expression Tree, compile it and evaluate the results.

I would go a little deeper, but this site does a much better job:

Expression trees as a compiler

Examples include creating generic operators for variable types, manual lambda expressions, high-performance shallow cloning, and dynamically copying read / write properties from one object to another.

Summary
Expression trees are representations of code that is compiled and evaluated at runtime. They allow the use of dynamic types, which are useful for data processing and dynamic programming.

+3
Aug 6 '13 at
source share

Is the expression tree you are referencing the expression evaluation tree?

If so, then this is a tree built by the parser. Parser used Lexer / Tokenizer to identify tokens from the program. Parser creates a binary tree from tokens.

Here is a detailed explanation

-3
Mar 08 '09 at 11:18
source share



All Articles