[Go] Create an expression from the simple to the deep Expression Tree (1 ).
Why Learning Expression Tree? The Expression Tree stores the logic that can be directly written by code in the tree structure as an expression, so that the tree can be parsed at runtime and then executed, dynamically edit and execute code. The Expression Tree is translated into SQL. Therefore, understanding the Expression Tree helps us better understand the SQL statements. If you are interested, you can use it to create many interesting things.
The Expression Tree was introduced with. NET 3.5, so it is not a new technology. But I don't know how many people have a thorough understanding of it. In the previous Lambda expression reply, I found that everyone is quite interested in Lambda expressions and expression trees, let's take a good look at the good things that have created LINQ to SQL and made LINQ to Everything.
This series has three plans. The first section describes how to create an Expression Tree. The second article mainly introduces the traversal of the Expression Tree. Article 3 uses the expression tree to create a LinqProvider.
- From simple to deep Expression Tree (I) create an Expression Tree
- From simple to deep Expression Tree (2) traversing Expression Tree
- Expression Tree from simple to deep (3) Linq to blog
Main content of this article:
- Create a simple expression tree with Lambda expressions
- Manually create a complex Expression Tree
- Expression Tree Type list and Examples
Create a simple Lambda Expression Tree
In the previous Lambda expression, we mentioned that you can directly create an Expression Tree Based on Lambda expressions. This should be the most direct way to create an Expression Tree.
Expression <Func <int, int> expr = x => x + 1; Console. writeLine (expr. toString (); // the following code is compiled without Expression <Func <int, int, int> expr2 = (x, y) =>{ return x + y ;}; Expression <Action <int> expr3 = x => {};
But don't think too well. This method can only create the simplest expression tree, And the compiler of the complex points will not recognize it.
The right side is a Lambda expression, while the left side is an expression tree. Why can I assign values directly? This is thanks to our Expression <TDelegate> generic class. Expression <TDelegate> directly inherits from LambdaExpression. Let's take a look at the constructor of Expression:
internal Expression(Expression body, string name, bool tailCall, ReadOnlyCollection<ParameterExpression> parameters) : base(typeof(TDelegate), name, body, tailCall, parameters){}
In fact, this constructor does not do anything. It only transmits the relevant parameters to the parent class, that is, LambdaExpression, which stores the subject, name, and parameters of our expressions.
Expression<Func<int, int>> expr = x => x + 1;Console.WriteLine(expr.ToString()); // x=> (x + 1)var lambdaExpr = expr as LambdaExpression;Console.WriteLine(lambdaExpr.Body); // (x + 1)Console.WriteLine(lambdaExpr.ReturnType.ToString()); // System.Int32foreach (var parameter in lambdaExpr.Parameters){ Console.WriteLine("Name:{0}, Type:{1}, ",parameter.Name,parameter.Type.ToString());}//Name:x, Type:System.Int32
Simply put, Expression <TDelegate> generic classes are encapsulated to create a Lambda Expression Tree Based on Lambda expressions. There is a conversion process between them, and this conversion process occurs during our compilation. Do you still remember what we mentioned in Lambda expressions? Lambda expressions are a common method after compilation, while the Lambda tree is loaded into our runtime in a tree structure, only in this way can we traverse this tree at runtime. But why cannot we create a complex Expression Tree Based on Expression <TDelegate>? Next, let's look at it.
Create a complex Lambda Expression Tree
Next, let's create a complex Expression Tree step by step. Are you ready? As we mentioned above, the Lambda expression method is used to create an Expression Tree. Unfortunately, it is only applicable to one type. Next we will demonstrate how to create an expression tree without any parameters and no return values.
// The following method cannot be compiled:/* Expression <Action> lambdaExpression2 = () => {for (int I = 1; I <= 10; I ++) {Console. writeLine ("Hello") ;}}; * // create a loop Expression body that contains the code we want to execute LoopExpression loop = Expression. loop (Expression. call (null, typeof (Console ). getMethod ("WriteLine", new Type [] {typeof (string)}), Expression. constant ("Hello"); // create a code block Expression that contains the previously created loop Expression BlockExpression block = Expression. block (loop); // convert the above code Block Expression to <Action> lambdaExpression = Expression. lambda <Action> (block); lambdaExpression. compile (). invoke ();
In the preceding example, we created an Action without any parameters and executed a set of loops. The code is very simple. What's important is that we should be familiar with these expressions and their usage methods. The following types of expressions are introduced:
Does it look like a secret Expression Tree? If you execute the above code, it will be in an endless loop. I have no condition for adding break to the loop. To make it easier for everyone to understand, I am here to stop this loop. Just like the functions implemented by the code that cannot be compiled above, we need to output 10 "Hello ".
We have written a LoopExpression first, and then passed it to BlockExpresson to form a piece of code or we can also say a method body. However, if we have multiple execution blocks and these execution blocks need to process the same parameter, we have to declare these parameters in the block.
ParameterExpression number=Expression.Parameter(typeof(int),"number"); BlockExpression myBlock = Expression.Block( new[] { number }, Expression.Assign(number, Expression.Constant(2)), Expression.AddAssign(number, Expression.Constant(6)), Expression.DivideAssign(number, Expression.Constant(2)));Expression<Func<int>> myAction = Expression.Lambda<Func<int>>(myBlock);Console.WriteLine(myAction.Compile()());// 4
We declare an int variable and assign it to 2, then add 6 and divide it by 2. If we want to use a variable, we must declare it outside the block and introduce it into the block. Otherwise, an error occurs when the variable is not in the scope of the expression tree.
Next we will continue our unfinished work and add exit conditions for the loop. To help you quickly understand the exit mechanism of loop, let's look at a piece of pseudo code:
LabelTarget labelBreak = Expression. label (); Expression. loop ("if the condition is successful" "code executed successfully" "otherwise" Expression. break (labelBreak) // jump out of the loop, labelBreak );
We need to use LabelTarget and Expression. Break to exit the loop. Let's take a look at the real code:
LabelTarget labelBreak = Expression. label (); ParameterExpression loopIndex = Expression. parameter (typeof (int), "index"); BlockExpression block = Expression. block (new [] {loopIndex}, // initialize loopIndex = 1 Expression. assign (loopIndex, Expression. constant (1), Expression. loop (Expression. ifThenElse (// if judgment logic Expression. lessThanOrEqual (loopIndex, Expression. constant (10), // code Expression used to determine the logic. block (Expression. call (null, typeof (Console ). getMethod ("WriteLine", new Type [] {typeof (string)}), Expression. constant ("Hello"), Expression. postIncrementAssign (loopIndex), // judge the code Expression that fails. break (labelBreak), labelBreak); // returns the code block Expression <Action> lambdaExpression = Expression. lambda <Action> (block); lambdaExpression. compile (). invoke ();
I hope the above Code does not prevent you from learning Expression Tree's determination J.
Well, we have learned several new types of expressions to sum up:
Here, I think you should have a clear understanding of the construction of the Expression Tree. Why can't we directly create an Expression Tree Based on complicated Lambda expressions?
- The Lambda Expression here is actually an Expression Body.
- This Expression Body is actually one of the expressions we mentioned above.
- That is to say, the compiler needs time to analyze which one are you?
- The simplest x => x + 1 is Func <TValue, TKey>, which is easy to analyze.
- Actually, only BinaryExpression is allowed in the Expression Body.
Finally, let's take a complete look at the types of expressions that. NET provides for us (the following classes are inherited from expressions ).
TypeBinaryExpression
TypeBinaryExpression typeBinaryExpression = Expression.TypeIs( Expression.Constant("spruce"), typeof(int));Console.WriteLine(typeBinaryExpression.ToString());// ("spruce" Is Int32)IndexExpression
ParameterExpression arrayExpr = Expression.Parameter(typeof(int[]), "Array");ParameterExpression indexExpr = Expression.Parameter(typeof(int), "Index");ParameterExpression valueExpr = Expression.Parameter(typeof(int), "Value");Expression arrayAccessExpr = Expression.ArrayAccess( arrayExpr, indexExpr);Expression<Func<int[], int, int, int>> lambdaExpr = Expression.Lambda<Func<int[], int, int, int>>( Expression.Assign(arrayAccessExpr, Expression.Add(arrayAccessExpr, valueExpr)), arrayExpr, indexExpr, valueExpr );Console.WriteLine(arrayAccessExpr.ToString());// Array[Index]Console.WriteLine(lambdaExpr.ToString());// (Array, Index, Value) => (Array[Index] = (Array[Index] + Value)) Console.WriteLine(lambdaExpr.Compile().Invoke(new int[] { 10, 20, 30 }, 0, 5));// 15NewExpression
NewExpression newDictionaryExpression =Expression.New(typeof(Dictionary<int, string>));Console.WriteLine(newDictionaryExpression.ToString());// new Dictionary`2()
InvocationExpression
Expression<Func<int, int, bool>> largeSumTest = (num1, num2) => (num1 + num2) > 1000;InvocationExpression invocationExpression= Expression.Invoke( largeSumTest, Expression.Constant(539), Expression.Constant(281));Console.WriteLine(invocationExpression.ToString());// Invoke((num1, num2) => ((num1 + num2) > 1000),539,281)
Today, we demonstrated how to create an expression tree using code, and then summarized the expression types provided by. NET. Next, we will continue to study the traversal problem of the Expression Tree. Please stay tuned. If you are interested in the Expression Tree, please stay tuned ~,