Expression Tree Practice: Getting Started
[TOC]
What is an Expression Tree
Definition from Microsoft Official Documentation:
An expression tree represents code as a tree data structure.
What can it do?
You can edit and compute code in the expression tree. This makes it possible to dynamically modify executable code, execute LINQ queries across different databases, and create dynamic queries.
Is it fun?
Expression trees can also be used in the Dynamic Language Runtime (DLR) to provide interoperability between dynamic languages and the .NET Framework, while ensuring that compiler writers can emit expression trees instead of Microsoft Intermediate Language (MSIL).
Where is it applied?
ORM frameworks, workflow frameworks, etc., use code involving Lambdas... dynamic execution of code, dynamic assembly of code, and so on.
Creating Expression Trees
There are two ways to create expression trees: using lambda expressions and using APIs.
Creating an expression tree means that each node has been defined beforehand, and finally combining all nodes using code to generate the expression tree.
Example (Creating an expression tree using APIs)
```
ParameterExpression a = Expression.Parameter(typeof(int), "i");
ParameterExpression b = Expression.Parameter(typeof(int), "j");
Expression r1 = Expression.Multiply(a, b); // Multiplication
ParameterExpression c = Expression.Parameter(typeof(int), "x");
ParameterExpression d = Expression.Parameter(typeof(int), "y");
Expression r2 = Expression.Multiply(c, d); // Multiplication
Expression result = Expression.Add(r1, r2); // Addition
// The above code generates nodes
// Generate expression
Expression<Func<int, int, int, int, int>> func = Expression.Lambda<Func<int, int, int, int, int>>(result, a, b, c, d);
var com = func.Compile();
Console.WriteLine("Expression: " + func);
Console.WriteLine(com(12, 12, 13, 13));
Console.ReadKey();</code></pre>
The code above regarding expression trees is lengthy, and the following step is called generating/creating the expression tree.
Expression<Func<int, int, int, int, int>> func = Expression.Lambda<Func<int, int, int, int, int>>(result, a, b, c, d);
The following line is called executing the expression tree.
var com = func.Compile();
The other code is used to generate expression tree nodes/logics.
Returning to the main topic, the two methods for creating expression trees.
Creating Expression Trees with Lambda
The above example of expression trees is used to generate
( i * j ) + ( x * y )
However, writing such a long piece of code for such a simple operation is impractical.
Using lambda, it can be written as follows:
Expression<Func<int, int, int, int, int>> func = (i, j, x, y) => (i * j) + (x * y);
If using lambda to generate expression trees, lambda can only use single-line statements; control statements like if, for, etc. cannot be used.
Specifics about lambda expression trees will be explained in other articles later.
Creating Expression Trees via API
It’s that simple:
Expression<Func<int, int, int, int, int>> func = Expression.Lambda<Func<int, int, int, int, int>>(result, a, b, c, d);
On the left side, both methods look the same; the distinction lies in the right side of the equals sign.
Expression< TDelegate >
The final result of the examples above generates
Expression<Func<int, int, int, int, int>> func
func is the variable for the expression tree.
We can understand the methods and properties that the expression tree possesses.
The type used to generate expression tree nodes is Expression.
Thus, the created expression tree func is of type Expression<TDelegate>
.
Definition is as follows:
public sealed class Expression<TDelegate> : LambdaExpression
It possesses the following methods:

Method
Description
Compile()
Compiles the lambda expression described by the expression tree into executable code and generates a delegate representing the lambda expression.
Compile(Boolean)
Compiles the lambda expression described by the expression tree into interpreted or compiled code and generates a delegate representing that lambda expression.
Compile(DebugInfoGenerator)
Compiles the lambda into a method definition. (Inherited from LambdaExpression)
Update(Expression, IEnumerable)
Creates a new expression similar to this expression, but using the provided children. If all children are the same, this expression is returned.
Accept(ExpressionVisitor)
Dispatches to the specific Visit method for this node type. For example, MethodCallExpression calls VisitMethodCall.
Since Expression<TDelegate>
inherits from LambdaExpression
, many properties and methods can also be used.

Body
Gets the body of the lambda expression.
CanReduce
Indicates whether the node can be reduced to a simpler node. If true, Reduce() can be called to produce a simplified form.
Name
Gets the name of the lambda expression.
NodeType
Returns the node type of this Expression.
Parameters
Gets the parameters of the lambda expression.
ReturnType
Gets the return type of the lambda expression.
TailCall
Gets a value indicating whether the lambda expression will be compiled using tail call optimization.
Type
Gets the static type of the expression represented by this Expression.
Alright, consider the above as a small note for reference, currently not necessary, but will be gradually used later.
Parsing/Executing Expression Trees
After creating the expression tree, it needs to be executed.
Before that, you need to understand delegates Delegate, Func, Action, and their relationships.
Executing an expression tree is done as follows:
Expression<Func<int, int, int, int, int>> func = Expression.Lambda<Func<int, int, int, int, int>>(result, a, b, c, d);
var com = func.Compile();
var runRasult = com(12, 12, 13, 13);
func is merely an expression tree. After constructing the expression tree, to “convert the expression tree to code”, the .Compile()
method can be used to generate a delegate (like the above com).
For brevity, the above uses var, but it is actually like this:
Func<int,int,int,int,int> com = func.Compile();
Four parameters, one return value.
var runRasult = com(12, 12, 13, 13);
In C#, there is syntactic sugar; the delegate can be written like this:
Expression<Func<int, int, int, int, int>> func = Expression.Lambda<Func<int, int, int, int, int>>(result, a, b, c, d);
int runRasult = func.Compile()(12, 12, 13, 13);</code></pre>
From now on, we'll write like this wherever compressed one-line code is possible, and there's no need to write it out in two lines.
For debugging and viewing expression trees in VS, refer to:
Beginners need not be entangled in these; just understanding the content of this article and remembering the key points will suffice.
文章评论