Some of you are tool builders and extenders. You may more frequently write code generators using System.CodeDOM and System.Reflection.Emit, figure out clever ways to use functional construction for LINQ to XML or to write dynamic Lambda expression generators. Sometimes documentation is great or we Google and discover blogs like this one that help us and sometimes we have to figure out things through trial and error. (And, some of you some of you may just like to poke around and see what you can create instead of watching TV.) This quick holiday blog is for those of you—possibly a few—that may be interested in Lambda expressions and dynamic expression trees.
Things like Lambda expressions can be codified as expression trees. A reason for doing this might be that you want to write an an extender that pulls apart a Lambda expression and convert it into something else. A dynamic Lambda expression is created by invoking functions on the System.Linq.Expressions.Expression class, invoking a function for each element of the Lambda expression that goes into the expression tree. For example,
(s1, s2) => s1 == s2;
represents an equality test expression that accepts two strings and test them for equality. Represented as a tree there are two input parameter strings, an equality test, and the input parameters are used to generate a bool return value. If you were to assign this expression to the generic delegate Func then the signature for the delegate would be Func<string, string, bool>. Writing these straight forwardly as an expression one might logically write
LambdaExpression exp =
Expression.Lambda<Func<string, string, bool>>(
Expression.Equal(
Expression.Parameter(typeof(string), "str1"),
Expression.Parameter(typeof(string), "str2")),
new ParameterExpression[] { Expression.Parameter(typeof(string), "str1"),
Expression.Parameter(typeof(string), "str2")}
);
All of the elements are present:
Expression.Lambda<Func<string, string, bool>> – tags the expression as a Lambda expression with the correct signature
Expression.Equal – defines the equality test
Expression.Parameter(typeof(string), "str1") – defines the left-hand-side of the equality test
Expression.Parameter(typeof(string), "str2") – defines the right-hand-side of the equality test
and,
new ParameterExpression[] { Expression.Parameter(typeof(string), "str1"),
Expression.Parameter(typeof(string), "str2")} – defines the two input arguments whose names match the method body names.
Unfortunately, if you wrote the expression this way and compiled the expression you would get an InvalidOperationException (see Figure 1) with an error like “variable ‘str1’ of type System.String referenced from scope ‘’, but it is defined. Further, if you used the quick watch to look at the expression exp you would see the two parameters are present in the tree. There is a ‘justification’ for this and the correct code is shown in Listing 1.
Figure 1: This is a frustrating exception that does not tell you the solution.
The reason the fragment above does not work is that “Lambda expressions are fully bound and are not in the business of implementing name lookup rules (which may differ from language to language) [Anders Hejlsberg]”. The expression tree treats the name as informational and uses the referenced object to match up parameters and their use in a method body. This literally means you have to use the same object created with the Expression method static calls, such as Expression.Parameter, for the input parameters and their use in the method body. (The CodeDOM actually supplies two separate node constructors for arguments and references, but the Expression builders do not.) Listing 1 shows how the same two ParameterExpression objects are used to identify the method arguments and their use in the equality test.
In the example the expression tree is constructed, compiled, and invoked. You can use the debugger’s to QuickWatch feature to explore the LambdaExpression object or build and install the sample program ExpressionTreeVisualizer available online to extend Visual Studio and add a visualizer for expression trees. (Check out this link for more on the ExpressionTreeVisualizer http://msdn.microsoft.com/en-us/library/bb397975.aspx.)
Listing 1: Correctly associating input parameters by using the same object references when building the expression tree.
using System;
using System.Linq.Expressions;
namespace DynamicLambdas
{
class Program
{
static void Main(string[] args)
{
// Lambda expression tree don’t use name lookup rules
ParameterExpression str1 = Expression.Parameter(typeof(string), "str1");
ParameterExpression str2 = Expression.Parameter(typeof(string), "str2");
LambdaExpression exp =
Expression.Lambda<Func<string, string, bool>>(
Expression.Equal(
str1,
str2),
new ParameterExpression[] { str1, str2 }
);
Func<string, string, bool> compareStrings = (Func<string, string, bool>)exp.Compile();
string s1 = "devexpress";
string s2 = "Developer Express";
Console.WriteLine("'{0}' {1} '{2}'",
s1, compareStrings(s1, s2) ? "is the same string as" : "is not the same string as", s2);
Console.ReadLine();
}
}
}