Understanding and Using LINQ Expression Trees (
Page 1 of 3 )
Expression trees provide the means for parsing expressions sent to your
method and for implementing some special LINQ techniques.
Understanding and Using LINQ Expression Treesby John Paul Mueller
Expression trees provide the means for parsing expressions sent to your method and for implementing some special LINQ techniques.
ADVERTISEMENT
An expression tree in LINQ is just as its name implies—a tree structure that contains expressions. The expressions, in this case, are lambda expressions, the new method of defining a delegate in C# 3.0 (more on this later). The tree is a data structure that contains the compiled lambda expressions. Although expression trees may seem new and complex, they already exist in other languages. If you’ve ever worked with a language such as LISP, you know that the expression tree concept has a long history in computer science—it’s just that they’re new in C# 3.0.
You can use expression trees for just about any need. However, when it comes to LINQ, expression trees answer a specific need—they provide an efficient means for working with large data sources such as a database. However, as shown in this article, you can build an expression tree for any need should you wish to do so.
The important word for expression trees is dynamic. Expression trees let you make changes during runtime, rather than having to figure out everything during design time. When you create a lambda expression, the compiler can create either Intermediate Language (IL) code or an expression tree. In most cases, the compiler will create IL code because the LINQ provider you work with must provide the support required to create an expression tree. Consequently, unless you're using the LINQ to SQL provider in a generic LINQ setup, the compiler will likely create IL code from your lambda expression unless you specifically create an expression tree.
Considering Expression Tree Evolution
The evolution of the expression tree begins with the delegate. Yes, I know that this is stepping back a bit, but bear with me for a while. Understanding where expression trees started will really help you appreciate why they’re so cool. For the purposes of this article, the advantage of using a delegate is that you can substitute one method for another as needed. Look at the example in Listing 1.
Listing 1: Defining a Delegate
public delegate Boolean CheckLength(String Value);
public Boolean ShortString(String Value)
{
return Value.Length <= 3;
}
public Boolean LongString(String Value)
{
return Value.Length > 3;
}
public String CheckStrings(
String[] Strings, CheckLength Filter)
{
String Output = "";
// Validate the strings against the
// filter.
foreach (String ThisString in Strings)
if (Filter(ThisString))
Output = Output + ThisString + "\r\n";
// Return the strings that match.
return Output;
}
private void btnDelegate_Click(object sender, EventArgs e)
{
// Create a test array.
String[] TestStrings =
{"One", "Two", "Three", "Four", "Five", "Six"};
// Create an output variable.
String Message = "";
// Check for short strings.
Message = CheckStrings(
TestStrings, new CheckLength(ShortString));
MessageBox.Show(Message);
// Check for long strings.
Message = CheckStrings(
TestStrings, new CheckLength(LongString));
MessageBox.Show(Message);
}
In this case, the code begins with a delegate that defines the inputs and outputs for the two filter methods, ShortString() and LongString(), that follow. These filters are simple—they simply check the length of the input string and output true or false depending on what they find. The filters define a short string as 3 or less characters and a long string as anything over three characters.
The CheckStrings() method accepts an array of input strings and a filter it can use to check them. The code uses the filter to determine when a particular string in the array meets the specified criteria. The output from this method is a list of strings that meet the criteria.
The whole delegate setup is tested in btnDelegate_Click(). This method creates a test array and an output string. The code then tests the string array using the two different filters and displays the results on screen. Although this is a very simple example, it demonstrates the usefulness of delegates.
The problem is that this is a whole lot of code to demonstrate something supposedly simple. It turns out that C# 2.0 provides a partial solution to this problem by providing support for anonymous methods. Instead of defining the delegate, ShortString() method, and LongString() method, you can create the filter directly in the test portion of the code, as shown in Listing 2.
Listing 2: Using Anonymous Methods
private void btnAnonymous_Click(object sender, EventArgs e)
{
// Create a test array.
String[] TestStrings =
{ "One", "Two", "Three", "Four", "Five", "Six" };
// Create an output variable.
String Message = "";
// Check for short strings.
Message = CheckStrings(
TestStrings,
delegate(String Value)
{
return Value.Length <= 3;
});
MessageBox.Show(Message);
// Check for long strings.
Message = CheckStrings(
TestStrings,
delegate(String Value)
{
return Value.Length > 3;
});
MessageBox.Show(Message);
}
This is arguably much shorter. In addition, you can define the method as part of the call, rather than separately. This code achieves with two methods what required four methods and a delegate before. However, with C# 3.0 you can still do better. Lambda expressions make the check considerably shorter, as shown in Listing 3.
Listing 3: Using a Lambda Expression to Perform the Check
private void btnLambda_Click(object sender, EventArgs e)
{
// Create a test array.
String[] TestStrings =
{ "One", "Two", "Three", "Four", "Five", "Six" };
// Create an output variable.
String Message = "";
// Check for short strings.
Message = CheckStrings(
TestStrings, Value => Value.Length <= 3);
MessageBox.Show(Message);
// Check for long strings.
Message = CheckStrings(
TestStrings, Value => Value.Length > 3);
MessageBox.Show(Message);
}
The lambda expression condenses everything. The left is a parameter, Value, that you want to work with. It receives the input you provide from the array. The left side of the lambda expression, Value.Length <= 3, is actually composed of three elements: the property you want to work with, the comparison operator, and the constant value. Notice that there’s a tree-like structure already in place. You’ll see how this comes into play later in the article. The code is now short and relatively easy to read. It performs everything needed to check the string length in the shortest space possible.
Manipulating Society through Technology
Jeremy Bailenson, Director of the Virtual Human Interaction Lab at Stanford University, talks about virtual reality, avatars, Moore's law, how real world behaviors influence online reality, and societal manipulation through technology! >> Play video >> Read article >> See all videos