Preface
Plan to organize c# Code simplification History Series , So sort it out , Let's briefly introduce the concept .
What is an expression tree ?
The expression tree represents the code in a tree data structure , Each of these nodes is an expression , For example, method calls and x < y Such binary operations, etc .
What does this mean ? Using structure to represent code ? Use static to express dynamic , Generally speaking, it's some kind of agreement .
Like the strong and weak circuits in computers , It may not be easy to understand . Take an example of a box :
Suppose I'm going to add , What if it means addition ? I use a box structure , Put the first number in the first place , Put the second number in the second place , And then in the third position, I pass in the method , Indicates that the first and second methods will execute the third position , Here , Or structure , Because it didn't run , It's just a combination of such a structure .
Now? , Suppose that according to some kind of agreement, it is combined into a structure , So this is called an expression , It is used to express a certain situation . so what , Now this expression is a tree , So it's called an expression tree .
Here's the expression , To enhance a wave of :
And then dialysis a wave of :
Text
Let's use an example to show regular expression , An example is on the official website , But the explanation on the official website is vague , So explain the wave again .
An example used on the official website is :Where(company => (company.ToLower() == "coho winery" || company.Length > 16)).orderby(company=>company)
So take a look at it :
string[] companies = { "Consolidated Messenger", "Alpine Ski House", "Southridge Video", "City Power & Light",
"Coho Winery", "Wide World Importers", "Graphic Design Institute", "Adventure Works",
"Humongous Insurance", "Woodgrove Bank", "Margie's Travel", "Northwind Traders",
"Blue Yonder Airlines", "Trey Research", "The Phone Company",
"Wingtip Toys", "Lucerne Publishing", "Fourth Coffee" };
// The IQueryable data to query.
IQueryable<String> queryableData = companies.AsQueryable<string>();
I have an array , And then convert to IQueryable Format , The purpose of this is actually because queryable Achieved some expression Properties of .
ok , We will not explain the function of these parameters for the time being , It's easy to see from the back .
Then put the code :
ParameterExpression pe = Expression.Parameter(typeof(string), "company");
This means that you create a property that is company The variable of , Equivalent to what we used to be xy, Name at will .
Expression left = Expression.Call(pe, typeof(string).GetMethod("ToLower", System.Type.EmptyTypes));
Expression right = Expression.Constant("coho winery");
Expression e1 = Expression.Equal(left, right);
Because of the structure , So the parameters here left It's the left tree ,right It's the right subtree .
left Well , This call That is to say pe( That is to say company Variable ) A method will be executed ,ToLower, The corresponding is company.ToLower().
And then on the right is a fixed parameter coho winery, Now the expression is company.ToLower()=='coho winery', Back to a bool type .
left = Expression.Property(pe, typeof(string).GetProperty("Length"));
right = Expression.Constant(16, typeof(int));
Expression e2 = Expression.GreaterThan(left, right);
The next step is to get compay Properties of Length, And then int Type comparison , Namely conpany.length>16
Expression predicateBody = Expression.OrElse(e1, e2);
So that is e1 and e2 Connected to a , In the middle is or,company.ToLower() == "coho winery" || company.Length > 16 OK, now the expression is over , So how to connect with data ?
// Create an expression tree that represents the expression
// 'queryableData.Where(company => (company.ToLower() == "coho winery" || company.Length > 16))'
MethodCallExpression whereCallExpression = Expression.Call(
typeof(Queryable),
"Where",
new Type[] { queryableData.ElementType },
queryableData.Expression,
Expression.Lambda<Func<string, bool>>(predicateBody, new ParameterExpression[] { pe }));
// ***** End Where *****
// ***** OrderBy(company => company) *****
// Create an expression tree that represents the expression
// 'whereCallExpression.OrderBy(company => company)'
MethodCallExpression orderByCallExpression = Expression.Call(
typeof(Queryable),
"OrderBy",
new Type[] { queryableData.ElementType, queryableData.ElementType },
whereCallExpression,
Expression.Lambda<Func<string, string>>(pe, new ParameterExpression[] { pe }));
// ***** End OrderBy *****
// Create an executable query from the expression tree.
IQueryable<string> results = queryableData.Provider.CreateQuery<string>(orderByCallExpression);
whereCallExpression and orderByCallExpression Indicates the operation to be performed , use whereCallExpression For example, the target type is Queryable, call where, Then the expression is predicateBody, Parameter is pe.
orderByCallExpression Reason by analogy .
The last step is to deliver the expression :IQueryable
The implementation is very complicated , I didn't go to see it myself , Because there's no need to , This kind of thing is a tool , If anyone writes like this , It's just that the logic of thinking is not generally strong , Generally speaking, it's similar to assembly .
It needs to be understood that it will not be executed immediately , It's an expression that's connected to it . If you are interested, you can learn about iqueryable The implementation of the , A complex batch of .
So no matter how complicated it is , It's essentially a set of rules , We follow the rules of it and fill us with , Then the corresponding results will be given to us , We may not be able to design such a good expression , But sometimes we make rules , For example, a certain format, etc , But don't think about how elegant the code is , Because it's very stable , No need to pursue elegance .
So we're trying to delay the implementation of , Is that what we're going to do ? If you do , I think a lot of people will design another set of , It's not that complicated , Maybe just a few parameters , Then a delegate is combined into a tree , Despite its limitations , But it's really painful to write code like this .
At this time, people wonder if there is anything that can be changed in the middle ? For example, I write a string of characters , And then I will automatically follow some rules to analyze it , But one of the bad things about this is that , String is weak type, debugging is quite troublesome , This is the time to aim lambda 了 .
if lambda The expression is assigned to Expression<TDelegate> Variable of type , The compiler can emit code to create a representation of the lambda Expression tree of expressions .
C# The compiler can only start from expressions Lambda( Or one-way Lambda) Generate an expression tree . It can't parse statements lambda ( Or multiple lines lambda).
for instance :
Expression<Func<int, bool>> lambda = num => num < 5;
You can use lambda The expression performs a expression Transformation .
from Expression To Expression
Object
Expression
LambdaExpression
Expression<TDelegate>
A lot of times lambda Expression transformation can solve most problems for us , But don't think it's Expression All , Because there's only one line to convert , then expression There are a lot of things that don't work lambda To represent the .
To continue
To be continued .