lambda expressions and expression trees
Let's start with an evolutionary diagram of delegate conversions.
See Lambda simplifies the use of delegates.
Lambda can be implicitly converted to a delegate or an expression tree. Convert to a delegate, as in the following code:
func<stringint> getlength = s = = S.length;
Converting to an expression tree is the following code:
expression<func<stringint>> GetLength = s = = S.length;
All the things in front of the delegate are described in detail. We mainly study expression trees
An expression tree
Expressions are the most important component in today's programming languages. Simply put, an expression is a combination of variables, values, operators, and functions that represent a certain meaning. For example, these are expressions that are (C #):
3 //constant expression
a / / variable or parameter expression
!a // unary logical non-expression
a + b // binary addition expression
Math.Sin(a) // method call expression
New StringBuilder() //new expression
myString.length//MemberAccess expression
First, clarify a concept: an expression. An expression is an ending sentence, if it is a sentence, it is not an expression, for example, the following is not an expression:
{....}
The code as data is an ancient concept. NET3. The 5 expression tree provides an abstract way to represent some code as an object tree. It is similar to CodeDom, but operates at a slightly higher level. Expression trees are primarily used for LINQ, and this section later explains the importance of expression trees for the entire LINQ.
The System.Linq.Expressions namespace contains classes representing expressions that inherit from expression, an abstract class that primarily contains static factory methods that are used to create instances of other expression classes. However, the Expression class also consists of two properties.
- The Type property represents the. NET type after the expression is evaluated and can be treated as a return type. For example, if an expression is to get the length property of a string, the type of the expression is int.
- The NodeType property returns the kind of expression that is represented. It is a member of the Expressiontype enumeration, including LessThan, multiply, and invoke. Still use the above example for mystring. The Length of this property accesses its node type as memberaccess.
static void Main(string[] args)
{
Expression firstArg = Expression.Constant(2);
Expression secondArg = Expression.Constant(3); BinaryExpression add = Expression.Add(firstArg, secondArg);
Console.WriteLine(add);
Console.ReadKey();
}
The above results will eventually be output (2+3). This means that these expression tree classes override ToString to produce a readable output.
As you do in code, you create a leaf expression first, and then you create the complete expression from the bottom up. This is determined by the fact that "the expression is not easy to change"-after creating the expression, it will never change. This allows you to cache and reuse expressions as you wish.
LambdaExpression is one of the types derived from expression. The generic class expression<tdelegate> is also derived from lambdaexpression.
The difference between the expression and the Expression<tdelegate> class is that a generic class identifies what kind of expression it is, in the form of a static type, that is, it determines the return type and parameters. Obviously, this is represented by the tdelegate type parameter, which must be a delegate type. For example, suppose our simple addition expression is a delegate that does not get any arguments and returns an integer. The signature that matches it is func<int>, so you can use an expression <func<int>> to represent it in a statically typed way. We use the Expression.lambda method to accomplish this. The method has many overloaded versions-our example uses a generic method that specifies the type of the delegate we want to represent by using a type parameter.
static void Main(string[] args)
{
Expression firstArg = Expression.Constant(2);
Expression secondArg = Expression.Constant(3);
BinaryExpression add = Expression.Add(firstArg, secondArg);
Func<int> lambda = Expression.Lambda<Func<int>>(add).Compile();
Console.WriteLine(lambda());
Console.ReadKey();
}
EXPRESSION.LAMBDA has generic overloads that indicate that an expression can be converted to an expression of type argument func<int>, and then the expression tree can be compiled into a delegate by means of the compile method.
Impression. We created some logic blocks in the program (such as expression Firstarg = Expression.constant (2)), represented them as normal objects, and then asked the framework to compile everything into "real" code that could be executed. You may never need to really use expression trees in this way, and never even need to construct them in your program, but it provides quite useful background knowledge to help you understand how LINQ works.
The above is about compiling the expression tree into lambda, which is described below----
Convert a lambda into an expression tree
We know that a lambda expression can be explicitly or implicitly converted to an appropriate delegate instance. However, this is not the only conversion that can be made. You can also ask the compiler to build an expression tree from your lambda expression to create an instance of expression<tdelegate> at execution time . For example, the following shows an expression that creates a "return 5" in a much leaner way, then compiles the expression and invokes the compiled delegate.
static void Main(string[] args)
{
Expression<Func<int>> return5 = () => 5;
Func<int> lambda = return5.Compile();
Console.WriteLine(lambda());
Console.ReadKey();
}
Some limitations:
- Not * * all * * Lambda expressions can be converted into expression trees. You cannot convert a lambda with a block of statements (even if there is only one return statement) into an expression tree-only a lambda that evaluates a single expression can.
- An assignment operation cannot be included in an expression because it is not represented in an expression tree. Although. NET4 extends the functionality of the expression tree, but the restriction that only a single expression can be converted is still valid.
- There are other limitations, but rarely, you get compiler hints when compiling errors
More complex examples:
static void Main(string[] args)
{
Expression<Func<string, string, bool>> expression = (x, y) => x.StartsWith(y); var lambda = expression.Compile();
Console.WriteLine(lambda("first that i did","first"));//true
Console.ReadKey();
}
The above example is very complex if it is done with an expression tree:
Static void Main(string[] args)
{
MethodInfo method = typeof(string).GetMethod("StartsWith", new []{typeof(string)});//1 Constructs the various parts of this method call
Var target = Expression.Parameter(typeof(string), "x");
Var methodArg = Expression.Parameter(typeof(string), "y");
Expression[] methodArgs =new[] {methodArg};
Expression call = Expression.Call(target, method, methodArgs); //2 construct a callexpression from the above components
Var lambdaParameters = new[]{target, methodArg};//3 converts callexpression to lambda
Var lambda = Expression.Lambda<Func<string,string,bool>>(call,lambdaParameters);
Var compiled = lambda.Compile();
Console.WriteLine(compiled("first","second"));//false
Console.WriteLine(compiled("first", "fir"));//true
Console.ReadKey();
}
First, thank the compiler for allowing Lambda to be implicitly converted into an expression tree!
The only benefit is that it does show more clearly what is involved in the tree and how the parameters are bound. To construct the final method invocation expression, we need to know the ① of several parts of the method invocation, including: The target of the method (that is, the string that calls StartsWith); the method itself (MethodInfo); The parameter list (this example has only one parameter). In this case, the target and parameters of the method are exactly the arguments passed to the expression, but they are entirely possible other expression types, as usual, the result of other method calls, the evaluation result of the property, and so on. After you have constructed a method call into an expression, then you need to convert it to a lambda expression, and bind the parameter. We reused the value of a parameter expression created as a method invocation (part) information (parameterexpression): The order of the parameters specified when creating a lambda expression is the order of the parameters used when the delegate is finally invoked.
This is the expression tree after compilation.
Expression tree at the core of LINQ
Without a lambda expression, an expression tree has little value. To some extent, there is no expression tree, lambda is not so useful. The full representation of LINQ in C # includes the three parts of lambda, expression tree, and extension method.
For a long time, we can either perform a good check at compile, or instruct another platform to run some code that is generally expressed as text (such as SQL queries). However, the fish and bear can not be combined (this is the advantage of lambda, it can be converted into a delegate, in-process with an iterator to process the collection sequence, but also can be compiled into an expression tree for other providers to translate the language on another platform, such as SQL). A lambda expression provides the ability to check at compile time, whereas an expression tree can extract the execution model from the logic you need. After merging the two together, both the fish and the bear's paw can be combined-certainly within a reasonable category. The central idea of an out-of-process LINQ provider is that we can generate an expression tree from a familiar source language (such as C #), use the result as an intermediate format, and then convert it to a local language on the target platform, such as SQL. At some point, you're more likely to encounter a native API than a simple native language. For example, this API might invoke different Web services based on what the expression represents. Shows the different paths for LINQ to Objects and LINQ to SQL.
In addition to LINQ, Expression trees can be used elsewhere
1. When we discuss C # dynamic types in future content, we will see more about the dynamic language runtime. An expression tree is a central part of its architecture. They have three features that are particularly attractive to the DLR:
- They are immutable and therefore can be safely cached;
- They are composable, so complex behaviors can be constructed in simple blocks;
- They can be compiled into delegates, which can be further JIT-compiled into native code as usual.
2. You can safely refactor members ' references
In the future C # will introduce a infoof operator, but specifically what to do not know, here first mark. Later to add.
3. Other ....
Let's look at the introduction to the book again. It doesn't seem to work.
Type inference and change of overload resolution
The addition of lambda expressions in c#3 makes the original type inference and overload decisions change for the new environment.
C # Review notes (4)--c#3: Innovative Ways to write code (lambda expressions and expression trees)