Lambda expressions and expression trees

Source: Internet
Author: User

In C # 2.0, the implementation of the delegate is greatly simplified by means of group conversion and anonymous methods. However, anonymous methods are still bloated, and readability can be affected when the code is filled with anonymous methods. The lambda expression appearing in C # 3.0 further simplifies the delegation without sacrificing readability.

The basic function of LINQ is to create operational pipelines and any state that these operations require. These operations represent a variety of logic about the data, such as data filtering, sorting data, and so on. Typically, these operations are represented by delegates. A lambda expression is a language-compliant representation of a LINQ data operation.

Lambda expressions can not only be used to create delegate instances, but the C # compiler can also convert them into expression trees.

Let's look at the lambda expression first.

Lambda expression as a delegate

Lambda expressions can be thought of as a further evolution of the anonymous method of C # 2.0, so almost everything an anonymous method can do can be done with a lambda expression (note that anonymous methods can ignore parameters and lambda expressions do not).

Like an anonymous method, a lambda expression has a special translation rule: the type of the expression itself is not a delegate type, but it can be converted to a delegate instance by implicitly or explicitly sending that one. The term anonymous function covers both the anonymous method and the lambda expression.

Here's an example of using a lambda expression to get the length of a string, and you'll get more concise and readable code with lambda:

Static voidMain (string[] args) {    //get the string length using anonymous methods in C # 2.0func<string,int> strlength =Delegate(stringSTR) {returnStr. Length;    }; Console.WriteLine (Strlength ("Hello world!")); //using lambda expressions//(Explicit type argument list) = {statement},LAMBDA expression most verbose versionStrlength = (stringstr) = = {returnStr. Length;    }; Console.WriteLine (Strlength ("Hello world!")); //single expression as body//(Explicit type argument list) = = ExpressionStrlength = (stringstr) =Str.    Length; Console.WriteLine (Strlength ("Hello world!")); //implicitly typed argument lists//(implicit type argument list) = = ExpressionStrlength = (str) = =Str.    Length; Console.WriteLine (Strlength ("Hello world!")); //shortcut syntax for single parameters//parameter name-= ExpressionStrlength = str = =Str.    Length; Console.WriteLine (Strlength ("Hello world!"));}

"= =" is new in C # 3.0 and tells the compiler that we are using lambda expressions. "= =" can be read as "goes to", so the lambda expression in the example can be read as "Str goes to Str." Length ". As you can see from the example, we can further simplify the lambda expression, depending on the special case used by lambda.

Lambda expressions are most often used in conjunction with a delegate type that returns non-void (for example, func<tresult>). In C # 1.0, delegates are typically used for events and rarely return any results. In LINQ, a delegate is often treated as part of a data pipeline, accepting input and returning results, or judging whether an item conforms to the current filter, and so on.

Lambda expression Nature

Looking at the example above, you can see that the lambda expression is an anonymous method, and that the compiler helped us with the transformation, allowing us to use the lambda expression directly to further simplify the code that created the delegate instance.

Using lambda expressions in list<t>

Here's a quick introduction to what a lambda expression is, and here's an example to learn more about lambda expressions.

In the previous article, we also mentioned the List<t> method, such as the FindAll method, which is a delegate of type predicate<t>, the return result is a new filtered list, and the Foreach method gets a action< A delegate of type t>, and then sets the behavior for each element. Here's a look at using lambda expressions in list<t>:

 Public classbook{ Public stringName {Get;Set; }  Public intYear {Get;Set; }}classprogram{Static voidMain (string[] args) {        varBooks =NewList<book>        {            NewBook{name="C # Learning Guide", year=2005},            NewBook{name="C # Step by step", year=2005},            NewBook{name="Java Learning Guide", year=2004},            NewBook{name="Java Step by step", year=2004},            NewBook{name="Python Learning Guide", year=2003},            NewBook{name="C # in depth", year= -},            NewBook{name="Java in Depth", year= the},            NewBook{name="Python in Depth", year= -},        }; //create a delegate instance to represent a common operationaction<book> printer = book + Console.WriteLine ("Name = {0}, Year = {1}", book. Name, book.        year); Books.        ForEach (printer); //using lambda expressions to filter list<t>Books. FindAll (book = book). Year > .).        ForEach (printer); Books. FindAll ( Book= Book. Name.contains ("C #")).        ForEach (printer); //using lambda expressions to sort list<t>Books. Sort ((Book1, book2) =Book1.Name.CompareTo (Book2.        Name)); Books.        ForEach (printer);    Console.read (); }}

As you can see from the example above, we'd better create a delegate instance and invoke it repeatedly instead of using a lambda expression every time we use it (for example, the printer delegate instance in the example) when we are going to use an operation frequently.

Compared to the delegate in C # 1.0 or the anonymous function of C # 2.0, combined with a lambda expression, the data manipulation in list<t> is simple and easy to read.

An expression tree

Expression trees, also known as expression trees, represent the code as an object tree in an abstract way, and each node in the tree is itself an expression. An expression tree is not executable code, it is a data structure.

Let's look at how to build an expression tree from C # code.

Building an expression tree

The System.Linq.Expressions namespace contains classes that represent expressions, all of which derive from expression, and we can create an instance of an expression class from a static method in those classes. The expression class consists of two important attributes:

    • The Type property represents the. NET type of the evaluation expression, which can be treated as a return type
    • The NodeType property returns the type of expression that is represented

Let's look at a simple example of building an expression tree:

Expression NumA = Expression.constant (6); Console.WriteLine ("NodeType: {0}, Type: {1}", Numa.nodetype, Numa.type); Expression NumB= Expression.constant (3); Console.WriteLine ("NodeType: {0}, Type: {1}", Numb.nodetype, Numb.type); Binaryexpression Add=Expression.add (NumA, NumB); Console.WriteLine ("NodeType: {0}, Type: {1}", Add. NodeType, Add. Type); Console.WriteLine (add); Console.read ();

The output of the code is:

As you can see from the example, we have constructed an expression tree (6+3) and looked at the type and NodeType properties of each node.

Expression has many derived classes, and there are many node types. For example, binaryexpression represents any operation with two tree of operations. This is where the NodeType attribute is important to distinguish between different kinds of expressions represented by the same class. Other node types are not covered and are interested to refer to MSDN.

For the above example, you can describe the resulting expression tree, and it is worth noting that the "leaf" expression was first created in the code, and the expression was built from the bottom up. Expressions are immutable, and all expressions can be cached and reused.

Compiling an expression into a delegate

LambdaExpression is one of the types derived from expression. The generic type expression<tdelegate> is also derived from lambdaexpress.

The difference between expression and expression<tdelegate> is that a generic class flags in a static type what kind of expression it is, that is, it determines the return type and Parameters . For example, the addition example above, the return value is an int type, no parameters, so we can use the signature func<int> to match, so we can use expression<func<int>> Represents the expression in a statically typed manner .

The purpose of this is that LambdaExpression has a compile method that can create a delegate of the appropriate type. Expression<tdelegate> also has a method with the same name, which can return a delegate of type TDelegate. Once the delegate is obtained, we can execute the expression in the same way that the normal delegate instance is called.

Following the example of addition above, we convert the above addition expression tree to a delegate and then execute the delegate:

func<int> adddelegate = expression.lambda<func<int>>(add). Compile (); Console.WriteLine (AddDelegate ());

From this example we see how to construct an expression tree and then compile the object tree into real code. The expression tree in. NET 3.5 can only be a single expression and cannot represent a complete class or method. This has been improved in. NET 4.0, where expression trees can support dynamic types, we can create blocks, assign values to expressions, and so on.

To convert a lambda expression to an expression tree

The lambda expression not only creates a delegate instance, C # 3.0 provides built-in support for converting a lambda expression into an expression tree. We can use the compiler to convert a lambda expression into an expression tree and create an instance of expression<tdelegate>.

In the following example, we convert a lambda expression into an expression tree and view the parts of the expression tree through code:

Static voidMain (string[] args) {    //To convert a lambda expression to an expression tree of type expression<t>//expression is not executable codeexpression<func<int,int,int>> expression = (A, b) = + A +b;    Console.WriteLine (expression); //get the body of a lambda expressionBinaryexpression BODY =(binaryexpression) expression.    Body; Console.WriteLine (expression.    Body); //get the parameters of a lambda expressionConsole.WriteLine ("param1: {0}, Param2: {1}", expression. parameters[0], expression. parameters[1]); ParameterExpression Left=(parameterexpression) body.    Left; ParameterExpression Right=(parameterexpression) body.    right; Console.WriteLine ("Left body of expression: {0}{4} NodeType: {1}{4} right body of expression: {2}{4} Type: {3}{4}", left. Name, body. NodeType, right. Name, body.    Type, Environment.NewLine); //converts an expression tree to a delegate and executesfunc<int,int,int> adddelegate =Expression.compile (); Console.WriteLine (AddDelegate (Ten, -)); Console.read ();}

The output of the code is:

Use of expression trees

As I saw earlier, we can construct an expression tree through the various node types in the derived class of expression, and then we can convert the expression tree to the appropriate instance of the delegate type, and finally execute the code of the delegate instance. However, we do not execute the code of the delegate instance around such a large bend.

Expression trees are primarily used in LINQ to SQL, and we need to convert a LINQ to SQL query expression (return IQueryable type) to an expression tree. The conversion is required because the LINQ to SQL query expression is not executed in C # code, the LINQ to SQL query expression is converted to SQL, sent over the network, and finally executed on the database server.

Here is a simple introduction, followed by a description of LINQ to SQL related content.

compiler's handling of lambda expressions

As we learned earlier, lambda can be used to create delegate instances, or to build expression trees, which the compiler has done for us.

How the compiler decides to generate an executable il or an expression tree:

    • When a lambda expression is assigned a variable of a delegate type, the compiler generates the same IL as the anonymous method (the executable instance of the delegate)
    • When a lambda expression is assigned to a variable of type expression, the compiler converts it to a tree of expressions

Shows the different ways that a lambda expression is handled in LINQ to Object and LINQ to SQL:

Summarize

The lambda expression is described in this article, which simplifies the creation of the delegate instance on the basis of anonymous methods, and writes more concise and readable code. Anonymous functions are not equal to anonymous methods, and anonymous functions contain both anonymous and lambda expressions.

Not only can lambda create delegate instances, but it can also be translated by the compiler into an expression tree, allowing code to execute outside of the program (refer to LINQ to SQL).

Lambda expressions and expression trees

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.