Expression TreesLambda expressions provide a succinct syntax for defining a method inline within your code. The compiler converts the code so that it is executable andbool swap = first < second;if (swap){swapCount++;}return swap;}}LocalsDisplayClass_00000001 locals =new __LocalsDisplayClass_00000001();locals.swapCount=0;BubbleSort(items, locals.__AnonymousMethod_00000000);|Console.WriteLine("Items were swapped {0} times.",locals.swapCount);callable later, potentially passing the delegate to another method. One feature for which it does not offer intrinsic support, however, is a representation of the expression as data-data that may be traversed and even serialized.Consider the lambda expression in the following code:persons.Where( person => person.Name.ToUpper() == "INIGO MONTOYA"); Assuming that persons is an array of Persons, the compiler compiles the lambda expression to a Func<string, bool> delegate type and then passes the delegate instance to the Where() method. Code and execution like this work very well. (The Where() method is an IEnumerable extension method from the class System.Linq.Enumerable, but this is irrelevant within this section.)What if persons was not a Person array, but rather a collection of Person objects sitting on a remote computer, or perhaps in a database? Rather than returning all items in the persons collection, it would be preferable to send data describing the expression over the network and have the filtering occur remotely so that only the resultant selection returns over the network. In scenarios such as this, the data about the expression is needed, not the compiled CIL. The remote computer then compiles or interprets the expression data.Interpreting is motivation for adding expression trees to the language. Lambda expressions that represent data about expressions rather than compiled code are expression trees. Since the expression tree represents data rather than compiled code, it is possible to convert the data to an alternative format-to convert it from the expression data to SQL code (SQL is the language generally used to query data from databases) that executes on a database, for example. The expression tree received by Where() may be converted into a SQL query that is passed to a database, for example (see Listing 12.22).Listing 12.22: Converting an Expression Tree to a SQL where Clausepersons.Where( person => person.Name.ToUpper() == "INIGO MONTOYA");select * from Person where upper(Name) = 'INIGO MONTOYA';Recognizing the original Where() call parameter as data, you can see that it is made up of the following:
The Where() method takes this data and converts it to the SQL where clause by iterating over the data and building a SQL query string. However, SQL is just one example of what an expression tree may convert to. Both a lambda expression for delegates and a lambda expression for an expression tree are compiled, and in both cases, the syntax of the expression is verified at compile time with full semantic analysis. The difference, however, is that a lambda expression is compiled into a delegate in CIL, whereas an expression tree is compiled into a data structure of type System. Linq.Expressions.Expression. As a result, when a lambda expression is an expression lambda, it may execute-it is CIL instructions for what the runtime should do. However, if the lambda expression is an expression tree, it is not a set of CIL instructions, but rather a data structure. Although an expression tree includes a method that will compile it into a delegate constructor call, it is more likely that the expression tree (data) will be converted into a different format or set of instructions.System.Linq.Enumerable versus System.Linq.QueryableLet us consider an example that highlights the difference between a delegate and an expression tree. System.Linq.Enumerable and System.Linq.Queryable are very similar. They each provide virtually identical extension methods to the collection interfaces they extend (IEnumerable and IQueryable, respectively). Consider, for example, the Where() method from Listing 12.22. Given a collection that supports IEnumerable, a call to Where() could be asfollows:persons.Where( person => person.Name.ToUpper() =="INIGO MONTOYA");Conceptually, the Enumerable extension method signature is defined on IEnumerable<TSource> as follows:public IEnumerable<TSource> Where<TSource>(Func<TSource, bool> predicate);However, the equivalent Queryable extension on the IQueryable< TSource> method call is identical, even though the conceptual Where() method signature (shown) is not:public IQueryable<TSource> Where<TSource>(Expression<Func<TSource, bool>> predicate);The calling code for the argument is identical because the lambda expression itself does not have type until it is assigned/cast. Enumerable's Where() implementation takes the lambda expression and converts it to a delegate that the Where() method's implementation calls. In contrast, when calling Queryable's Where(), the lambda expression is converted to an expression tree so that the compiler converts the lambda expression into data. The object implementing IQueryable receives the expression data and manipulates it. As suggested before, the expression tree received by Where() may be converted into a SQL query that is passed to a database.Examining an Expression TreeCapitalizing on the fact that lambda expressions don't have intrinsic type, assigning a lambda expression to a System.Linq.Expressions.Expression< TDelegate> creates an expression tree rather than a delegate. In Listing 12.23, we create an expression tree for the Func<int, int, bool>. (Recall that Func<int, int, bool> is functionally equivalent to the ComparisonHandler delegate.) Notice that just the simple act of writing an expression to the console, Console.WriteLine(expression) where expression is of type Expression<TDelegate>, will result in a call to expression's ToString() method). However, this doesn't cause the expression to be evaluated or even to write out the fully qualified name of Func<int, int, bool> (as would happen if we used a delegate instance). Rather, displaying the expression writes out the data (in this case, the expression code) corresponding to the value of the expression tree.Listing 12.23: Examining an Expression Tree