Click here to Skip to main content
15,884,176 members
Articles / Programming Languages / C#

Lambda Expressions

Rate me:
Please Sign up or sign in to vote.
4.23/5 (9 votes)
24 Sep 2015CPOL5 min read 22.6K   24   6
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.

C#
// 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

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

Is transformed in this IL form

Image 1

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.

C#
// 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.

Image 2

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

C#
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.

C#
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, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer Barclays Capital
Czech Republic Czech Republic
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionI like expression Pin
Sacha Barber25-Sep-15 10:11
Sacha Barber25-Sep-15 10:11 
AnswerRe: I like expression Pin
Milan Matějka27-Sep-15 3:40
Milan Matějka27-Sep-15 3:40 
QuestionWhat's the point of the last example? Pin
SledgeHammer0124-Sep-15 19:00
SledgeHammer0124-Sep-15 19:00 
AnswerRe: What's the point of the last example? Pin
Milan Matějka24-Sep-15 20:38
Milan Matějka24-Sep-15 20:38 
GeneralRe: What's the point of the last example? Pin
SledgeHammer0125-Sep-15 6:58
SledgeHammer0125-Sep-15 6:58 
GeneralRe: What's the point of the last example? Pin
Milan Matějka27-Sep-15 3:49
Milan Matějka27-Sep-15 3:49 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.