Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Lambda Expressions

0.00/5 (No votes)
24 Sep 2015 2  
Representing Lambda Expressions as delegates and expression trees.

Introduction

A lambda expression is an unnamed method that with minimal syntax you can use to create either

  • A delegate instance. As we know a delegate is a type that safely encapsulates a method, similar to a function pointer in C and C++. It is very broadly used in C#.
  • An expression tree, of type Expression<TDelegate>. A tree represents written code inside the lambda expression itself. This allows the lambda expression to be analyzed and interpreted during the runtime.

In following paragraphs we'll try to explain differences between these two choices and explain how we can build delegate instance or expression tree from lambda expression. 

Lambda expressions as delegates

Lambda  expressions  can  be  understood as  an  evolution  of  anonymous  methods. They are actually a superset of anonymous methods, so all restrictions that apply to anonymous methods also apply to lambda expressions. Huge benefit is they are almost  always  more  readable  and  shorter. Basically, you can do (almost) everything in an lambda expression that you can do in a normal method body. But to name few restrictions

  • You can't use break, goto or continue to jump from the lambda expression scope.
  • No unsafe code can be executed inside a lambda expression.
  • You cannot use contravariance. You have to specify the parameter types.

The parameters of an expression can be explicitly or implicitly typed. But the return type of the lambda must be implicitly convertible to the return type of the delegate. Lambda  expressions  have  a  lot  of  shortcuts  and  that will make  code very compact. They have  special  conversion  rules.

Shortly let's have a look how lambda expressions are translated by compiler in compile time. These lines basically represents which work compiler does step by step.

// Declare simple function returning length of string.
Func<string, int> f = text => text.Length;

// 1. step - Adding parentheses. Because for one parameter they are optional.
Func<string, int> f = (text) => text.Length;

// 2. step - From left side compiler can easily guess type of input parameter.
Func<string, int> f = (string text) => text.Length;

// 3. step - Adding another parentheses and checking whether type of return value corresponds left side.
Func<string, int> f = (string text) => { return text.Length; };

// 4. step - Now is actually very easy convert expression into delegate.
Func<string, int> f = delegate (string text) { return text.Length; };

We should point out once again that these steps are happening during compilation (not run) time. You can understand it like syntactic sugar for readable and compact code.

We can think that work for compiler finished here. But it is not right. Compiler is still generating IL code. But the compiler will create method within the existing class. Then this method is used when it creates the delegate instance -  just as if it were a normal method.

So basically this simple code

namespace LambdaExpressions
{
    class Program
    {
        static void Main(string[] args)
        {
            Func<string, int> f = text => text.Length;
        }
    }
}

Is transformed in this IL form

These methods have names not valid in C#, but they are valid in IL. It is preventing to refer them directly in your C# code and also it avoids the possibility of naming collisions. 

First very important thing to notice is that from our lambda expression was created an internal method. There is no such concept as “lambda expression method” in IL, instead a new method with your code is created.

Second thing to notice is strange mechanism of initializing of this delegate. It is cleverness of Microsoft compiler. Because if code is ever called again, this delegate is already cached and ready to use. So if you need to cache the delegate in your application, you actually don't have to. Microsoft compiler will do job for you for free.

Expression trees

C# 3 provides built-in support for converting lambda expressions into expression trees. But before that we will need to know what expression trees actually are.

Expression trees represent code in a tree-like data structure, where each node is an expression itself. This provide an abstract way of representing some code. This can be very valuable if you want to modify or transform code before executing it. Expression trees are used for example in the dynamic language runtime (DLR) to provide interoperability between dynamic languages and the .NET Framework or execution of LINQ queries.  

There are different types of expressions represent the different operations that can be performed. For example expressions for operations such as addition, comparison, negate, condition, method call, loop etc. We don't need to go through all of them; the whole list can be found on MSDN.

In the namespace System.Linq.Expressions can be found many classes, interfaces and enumerations. The most important class is the Expression class, which is in fact abstract class . Mostly it consists static factory methods to create instances of expressions classes.

Let’s start off by creating very simple expression tree which will tell us whether number is negative.

// Create input parameter expression.
ParameterExpression numberParameter = Expression.Parameter(typeof(int), "n");

// Constant representing 0 value.
ConstantExpression zero = Expression.Constant(0, typeof(int));

// Now create node consisting of input parameter and 0 value.
BinaryExpression numberLessThanZero = Expression.LessThan(numberParameter, zero);

// Define expression and input parameters.
Expression<Func<int, bool>> isNegativeExpression = Expression.Lambda<Func<int, bool>>(
        numberLessThanZero,
        new ParameterExpression[] { numberParameter });

// Before using it we need to compile just written code into delegate instance.
Func<int, bool> compiled = isNegativeExpression.Compile();

// Now we can finally test newly created method.
Console.WriteLine(compiled(-1));    // True
Console.WriteLine(compiled(0));     // False
Console.WriteLine(compiled(1));     // False

This is very easy example. By calling overridden method ToString() will produce human-readable output. Result is exactly what we expected.

As you see from example this is painful way to create just very simple tree expression. Luckily the compiler can help us. Answer for this trouble is lambda expression as we will see in next paragraph.

Before leaving this short description, we have to mention one very important fact - expressions are immutable. Once you create some, it’ll never change, so you can cache it and reuse for later.

Lambda expressions as expression trees

As we’ve already seen, lambda expressions can be converted to delegate instances. Second available conversion is to expression trees. Compiler is able build Expression<TDelegate> at execution time. Let's have a look on some example

Expression<Func<int, bool>> isNegativeExpression = n => n < 0;

That's it, nothing more. Compare it with example from previous paragraph. This notation is short and readable. We can finish same example like before.

Expression<Func<int, bool>> isNegativeExpression = n => n < 0;

Func<int, bool> compiled = isNegativeExpression.Compile();

// Now we can finally test newly created method.
Console.WriteLine(compiled(-1));    // True
Console.WriteLine(compiled(0));     // False
Console.WriteLine(compiled(1));     // False

On first line we created expression tree from lambda expression. On next line we compiled this to delegate instance. This compilation happens during the run (not compilation) time. And then on rest of lines we call this delegate to test it.

Note that not all lambda expressions can be converted to expression trees. There are couple of restrictions. But the most important is you cannot convert a lambda expression with a block of statements into an expression tree. Neither just one return statement. This restriction applies to all .NET versions.

References

History

Initial release v1.0: 24th September 2015

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here