Building Lambda Expressions from Expression Trees

Introduction

Sometimes things appear to be tricky until you know the
trick. Magic is like that, and some aspects of the .NET Framework are
like that. In this article you can learn a little more about
Lambda expressions, including how to dynamically construct a
Lambda expression using function construction. The reality
is it may be rare that you use construct Lambda expressions this way,
but it is cool, and learning more about Lambda expressions
may help the .NET developer understand them and use them in
their code a little better and a little more often.

When you use functional construction you basically have
to call a function for each element of the expression you
want to construct. For Lambda expressions this includes
parameters, constants, binary operations, and anything else
you want generated. The methods that let you dynamically
construct Lambda expressions are defined in the
System.Linq.Expressions namespace.

Reviewing Lambda Expressions

The historical progression to Lambda expressions is
function pointer, delegate,
anonymous method, and finally Lambda
expression. In short, a Lambda expression is a short hand
notation for a function. The biggest difference is that most
of the overhead for writing a function is eliminated with
Lambda expressions. Here is a function that performs a
simple binary less than or equal to operation followed by
the same behavior written as a Lambda expression.


Function LessThanOrEqualTo(ByVal i As Integer) As Boolean
Return i <= 7
End Function

Dim lambda As Func(Of Integer, Boolean) = Function(i) i < 7


Both the function LessThanOrEqualTo and the
Lambda expression (assigned to the generic delegate) lambda
provide the same solution.

Building a Lambda Expression Dynamically

To write a Lamba expression you use the Function keyword,
identify input parameters (and optionally the data type for
each parameter) a method body that returns a value. To
explore the constituent elements of a Lambda expression
assign an expression to an instance of Expresssion(Of
Delegate)
type. If you want to dynamically build a
Lambda expression–and explore the various decomposed
elements–you use functional construction and define an
Expression object that represents each element of the
expression. If you want to execute a dynamic Lambda
expression then call the Expression.Compile
method. Compile will return an instance of the expression
tree as a delegate that can be invoked.

The System.Linq.Expressions.Expression class
is the base class for all of the expression elements. The
functional methods that build the various elements, like
constants, are shared methods of the Expression
class. In Listing 1, a Lambda expression Function(i) i <
33 is dynamically constructed, the expression tree’s state
is dumped using a LINQ query, the expression is compiled and
invoked.


Imports System.Collections.Generic
Imports System.Linq
Imports System.Linq.Expressions
Imports System.Reflection

Module Module1

Sub Main()

‘ doesn’t do name look up on i so we have to use the same parameter
‘ expression to associate the input parameter with the parameter in
‘ the lambda body
Dim param As ParameterExpression = Expression.Parameter(GetType(Integer), “i”)

Dim exp As LambdaExpression =
Expression.Lambda(Of Func(Of Integer, Boolean))(
Expression.LessThanOrEqual(
param,
Expression.Constant(33)),
param
)

Dim mask As String = “{0} : {1}”
Dim results = From prop In exp.GetType().GetProperties()
Select String.Format(mask, prop.Name, prop.GetValue(exp, Nothing))

For Each item In results
Console.WriteLine(item)
Next

Dim lambda As Func(Of Integer, Boolean) = CType(exp.Compile(), Func(Of Integer, Boolean))

Console.WriteLine(“{0}: {1}”, exp.Body, lambda(10))
Console.ReadLine()

End Sub

End Module




Listing 1: A dynamically constructed and executed Lambda expression.

The example was written in microsoft VIsual Studio 2010.
The first thing to note is that VB2010 no longer requires
the use of the line continuation character. (I won’t miss
it.) Next notice that the ParameterExpression
param is defined all by itself.
ParameterExpression represents the input
argument i and its use in the function body. This is a
special “trick” (that is poorly documented in the help
files) that you have to know to use Lambda expression
arguments. See, the name of a parameter exists for
informational purposes only. Name lookups do not happen when
the Lambda expression is executed. However, by using the
same argument-param-for both the method body constructor and
the parameter bit the expression compiler will match up the
parameter with its use in the method body. If you define the
expression tree using a separate call for the method body
and input parameter as follows:


Dim exp As LambdaExpression =
Expression.Lambda(Of Func(Of Integer, Boolean))(
Expression.LessThanOrEqual(
Expression.Parameter(GetType(Integer), “i”),
Expression.Constant(33)),
Expression.Parameter(GetType(Integer), “i”)
)

Then when you attempt to invoke the compiled expression
you will get an InvalidOperationException (see
Figure 1) even though the expression will indicate that the
i-parameter is present the expression will fail. Remember
this is because the parameter name is not used to lookup
parameters, instead parameter inference is used.


Figure 1: Use a single ParameterExpression instance for all
parameters and their subsequent use in the method body.

The LambdaExpression object is constructed
of four parts: a LessThanOrEqual binary
expression containing a ParameterExpression and
a Constant expression and the input parameter,
which is the last argument of the function
Expression.Lambda. After the
LambdaExpression object is created the
expression tree will basically contain the Lambda expression
Function(i) i < 33.

The statement beginning with Dim results uses a LINQ
query to define an object state dumper; with the state
dumper and the For Each loop the constituent
elements of the LambdaExpression object will be
sent to the console (see Figure 2).



Figure 2: The state of the LambdaExpression object (with the
results shown on the last line).

The statement Dim lambda as Func(of Integer, Boolean)
accepts the return results from
LambdaExpession.Compile–as represented by
exp.Compile. In a nutshell the Lambda
expression tree is compiled and assigned to a local delegate
variable and ready to call. The final two lines invoke the
dynamic Lambda expression, displaying the results, and
waiting for the user to press the Enter key.

Summary

Before I wrote this article I didn’t know that the string
name of a parameter expression was for informational
purposes only. After reading a blog response from Anders
Hejjlsberg the reason makes sense–although the help files
are weak on this subject. Here is part of Anders’ response:
“Parameters are referenced in expressions through their
object identity, not by comparing names. In fact, from an
expression tree’s point of view the name of a parameter is
purely informational. The reason for this design is the same
reason that types are referenced through their
System.Type objects and not their names-
expression trees are fully bound and are not in the business
of implementing name lookup rules (which may differ from
language to language).” So, even if you never write your own
production Lambda generator a greater knowledge of how
things work and why, prepares you professionally for
unanticipated needs.

About the Author

Paul Kimmel is the VB Today columnist for CodeGuru and has written
several books on object-oriented programming and .NET. Check
out his upcoming book Professional DevExpress ASP.NET
Controls
(from Wiley) now available on Amazon.com and
fine bookstores everywhere. Look for his upcoming book
Teach Yourself the ADO.NET Entity Framework in 24
Hours
(from Sams). You may contact him for technology
questions at pkimmel@softconcepts
.com
. Paul Kimmel is a Technical Evangelist for
Developer Express, Inc, and you can ask him about Developer
Express at paulk@devexpress.com
and read his DX blog at http://
community.devexpress.com/blogs/paulk
.

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read