One of the core components that underlies .NET Language-Integrated Query (LINQ) are expression trees.
LINQ statements operate on types of IEnumerable
The LINQ statement is examined and turned into a type of expression object that presents the code as data that can be inspected; from there, the LINQ provider is able to look at the expression, determine how to execute the request, and then provide results.
In this column, I look at what an expression tree is, and how you might use it in your projects.
To say that an expression tree "presents the code as data that can be inspected" means that you create can expression object and each of its properties represents code or information about code.
For example, a BinaryExpression object represents an expression that has a property for the "left side" parameter and the "right side" parameter, which can be examined for things such as the type of the parameter and the name. As a result, you can "drill down" through an expression's properties (with a normal recursive tree walking algorithm) and look at every part of the expression at the lowest level.
Expression objects can be created from existing code or manually composed; the latter case is where a lot of the objects' utility comes into play. By writing code that creates expressions based on runtime conditions, you can do all sorts of tricks, including making a domain-specific language with its own syntax (an example that I am building towards in a series of articles).
An expression object is not just a lifeless collection of data, though; it can be compiled at runtime to present an actual function that can be run. While expressions are immutable, there is no reason why you cannot take an existing expression and copy its parts into a new one and make needed changes along the way.
Here's a simple application that creates an expression at runtime and consumes it:
using System;
using System.Linq.Expressions;
namespace TestProject
{
class Program
{
public static Func
{
var numberToAdd = Expression.Parameter(typeof(int), “number”);
var addAmount = Expression.Constant(amountToAdd, typeof(int));
var binaryExpression = Expression.Add(numberToAdd, addAmount);
var lambda = Expression.Lambda
return lambda.Compile();
}
static void Main(string[] args)
{
var addAmount = int.Parse(args[0].ToString());
var argument = int.Parse(args[1].ToString());
Console.WriteLine(CreateAdderFunction(addAmount).Invoke(argument));
Console.ReadLine();
}
}
}
In this application, we have a method (CreateAdderFunction) that returns a Func
Next, we define a ConstantExpression object (addAmount) of type int and assign it the value amountToAdd (the parameter passed to the method). Then a BinaryExpression object with a node type of Add is created; the left side of this object is the numberToAdd ParameterExpression, and the right side is the addAmount ConstantExpression.
After this, a LambdaExpression is made, with the signature of Func
Finally, we compile the LambdaExpression and return it, which gives us the Func
This is a very roundabout way of constructing the equivalent of the following:
public static int Adder(int number)
{
const int numberToAdd = 5;
return numberToAdd + number;
}
But, in the case of our expression version, the value of "numberToAdd" is determined at runtime and within code. This is a very simple example, but it illustrates the point well.
With an expression, we could create new functionality at runtime. Granted, this has been in .NET for some time with things like System.Reflection.Emit, but expressions are easier to work with than previous runtime function creations.
Also, you can see from how we composed the function how it could be useful to read the data of an expression built from an existing function. Code can be made to analyze an expression and take action based on what it finds.
Expressions trees are a pretty advanced topic, and you won't find a use for them in every project. I hope this overview whets your interest in expression trees and introduces you to a technique that you may want to learn more about. I may use expression trees in my RPN calculator project, so stay tuned for more on this topic.