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.



About the Author

Paul Kimmel

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.

Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • On-demand Event Event Date: September 10, 2014 Modern mobile applications connect systems-of-engagement (mobile apps) with systems-of-record (traditional IT) to deliver new and innovative business value. But the lifecycle for development of mobile apps is also new and different. Emerging trends in mobile development call for faster delivery of incremental features, coupled with feedback from the users of the app "in the wild." This loop of continuous delivery and continuous feedback is how the best mobile …

  • A modern mobile IT strategy is no longer an option, it is an absolute business necessity. Today's most productive employees are not tied to a desk, an office, or a location. They are mobile. And your company's IT strategy has to be ready to support them with easy, reliable, 24/7 access to the business information they need, from anywhere in the world, across a broad range of communication devices. Here's how some of the nation's most progressive corporations are meeting the many needs of their mobile workers …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds