Click here to Skip to main content
15,867,330 members
Articles / Programming Languages / C#
Article

Exploring Lambda Expression in C#

Rate me:
Please Sign up or sign in to vote.
4.61/5 (78 votes)
12 Mar 2008CPOL9 min read 461.2K   190   23
This article covers things from syntax to constraints and implementation details for lambda expression in C#

Introduction

Lambda expression is an inline delegate introduced with C # 3.0 language. It’s a concise way to represent an anonymous method. It provides a syntax to create and invoke functions. Although Lambda expressions are simpler to use than anonymous methods, they do slightly differ on how they are implemented. Both anonymous methods and Lambda expressions allow you define the method implementation inline, however, an anonymous method explicitly requires you to define the parameter types and the return type for a method. Lambda expression uses the type inference feature of C# 3.0 which allows the compiler to infer the type of the variable based on the context.

Lambda expression can be broken down into parameters followed by execution code. e.g.:

C#
Parameter => executioncode.

The left hand side represents zero or more parameters followed by the lambda symbol => which is used to separate the declaration of a parameter from the implementation of the method. Lambda expression is then followed by the statement body.

Lambda expression allows us to pass functions as arguments to a method call. I will start with a simple example of lambda expression which returns even numbers from a list of integers.

C#
//simple example of lambda expression.
    public static void SimpleLambdExpression()
    {
        List<int> numbers = new List<int>{1,2,3,4,5,6,7};
        var evens = numbers.FindAll(n => n % 2 == 0);
        var evens2 = numbers.FindAll((int n) => { return n % 2 == 0; });
        ObjectDumper.Write(evens);
        ObjectDumper.Write(evens2);
    }

Looking at the first lambda expression assigned to the evens variable, you will notice few things that are different from anonymous methods. First, we are not using delegate keyword anywhere in our code. Second, we are not defining the parameter and return types because the compiler infers the type based on context. The types in the expression are determined by the delegate definition. So in this case return type specified by the FindAll method takes a delegate which takes an int parameter and returns boolean. Lambda expression without braces and return type, provides the most concise way to represent anonymous methods. If the number of parameters is one then you can omit the parentheses surrounding the parameter as demonstrated in the first lambda expression. Although lambda expression does not require explicit parameters, you have the option to define the parameters, braces and return type as shown in the second lambda expression assigned to the even2 variable. Notice that we are using the explicit parameter of int and also use the return type as we usually specify in a method. The return statement would not work if you do not enclose the execution code with parentheses considering that you are fully qualifying everything that attributes a method.

Another place where parentheses are required in lambda expressions is when you want to use a parameter in multiple blocks of code inside the lambda expression such as follows:

C#
delegate void WriteMultipleStatements(int i);
    public static void MultipleStatementsInLamdas()
    {
        WriteMultipleStatements write = i =>
            {
                Console.WriteLine("Number " + i.ToString());
                Console.WriteLine("Number " + i.ToString());
            };
            write(1);
    }

image001.png

In the above code sample, we have enclosed our code in curly brackets so that we can use the parameter in both the expressions. If there were no curly brackets, the compiler wouldn't be able to recognize the variable i.

You can use lambda expressions where a delegate may not have any parameter. In that case, you have to supply a pair of empty parentheses to signify a method with no parameter. Here is a simple example which illustrates a lambda with no parameter.

C#
delegate void LambdasNoParams();
    public static void LambdasWithNoParameter()
    {
       LambdasNoParams noparams = () => Console.WriteLine("hello");
       noparams();
    }

image002.png

C# 3.0 defines the number of generic delegates that you can assign to your lambda expression instead of var keyword which infers the type. Let's take a look at an example of using a few of those generic delegates:

C#
public static void GenericDelegates()
    {
        List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7 };
        Func<int, bool> where = n => n < 6;
        Func<int, int> select = n => n;
        Func<int, string> orderby = n =>  n % 2 == 0 ? "even" : "odd";
        var nums = numbers.Where(where).OrderBy(orderby).Select(select);
        ObjectDumper.Write(nums);
    }

In the above example, we are using three different extension methods: where, orderby and select. The where extension method takes a generic delegate with int parameter and a return type of boolean to identity if a certain element be included in the output sequence. The select extension method takes an integer parameter and returns an integer, but it could return anything that you want the result to be transformed into — before being sent to the output sequence. In the orderby extension method, we are taking the integer parameter and using it to identify if it is even or odd. Based on that, we sort the results. It would have been extremely cumbersome if we had to define three different delegates for each of these lambda expressions. With the introduction of generic delegates in C# 3.0, it is fairly non trivial to assign our lambda expressions to generic delegates and pass those delegates to the extension methods. Generic delegates come in pretty handy and help you avoid writing common delegates which was common in .NET 1.1 and .NET 2.0 (since there were no generic delegates that came out of the box). Generic delegates allow you to define up to 4 parameters and 1 return type so you can have a delegate that may look something like this:

C#
Func<int, bool, string, double, decimal> test;

If your method or delegate does not meet the criteria then you have to manually declare a delegate that takes those parameters. Generic delegates usually cover majority of scenarios but in cases where it does not meet your needs, feel free to write a custom delegate.

There are cases where type inference may not return the data type that you really want the lambda expression to return. In those cases, we can explicitly specify the parameter type on the lambda expression. For example:

C#
Func<double, int> expr = (x) => x / 2;

The above expression returns a compiler error because by dividing a double, the inferred type is actually going to be double. However, you are assigning the lambda expression to a delegate that has a return type of int. If int is what you really want to return from the method, then you are better off casting the expression body to int to indicate your intent as shown below:

C#
Func<double, int> expr = (x) => (int)x / 2;

Lambda expressions are basically of two types. One is considered a simple expression where everything is inferred and consists of an expression only. The second type of lambda expression is statement blocks which are composed of braces and return type. Let's write a lambda expression in both forms to see the difference:

C#
//example showing two types of lambda expressions
public static void ExplicitParametersInLambdaExpression()
{
    Func<int, int> square = x => x * x;
    Func<int, int> square1 = (x) => { return x * x; };

    Expression<Func<int, int>> squareexpr = x => x * x;

    Expression<Func<int, int>> square2 = (int x) => { return x * x; };//does not compile.
}

Let's go ahead and dissect each lambda expression one at a time. The first lambda expression is considered a simple expression which does not have a statement body because there is no return statement and braces whereas the second lambda statement includes a statement body because it has a return statement and braces. Although both get compiled to a delegate, the benefit of lambda expressions without a statement body is that it can be converted to an expression tree which a certain provider can use to generate its own implementation. This is much like LINQ to SQL will convert the expression tree to its domain specific language called SQL, and send it to the database. The third lambda expression is really where lambda expression shies from an anonymous method. The beauty of this statement is the fact that you can easily convert to an expression whereas anonymous methods can only be converted to delegates. What is also great is that an expression can be converted back to a delegate by compiling the expression to delegate with the following syntax:

C#
Func<int,int> sq = squareexpr.Compile();

The last lambda expression raises an exception because the compiler cannot convert a lambda expression that contains a statement body, as you can observe from the fact that it is surrounded by braces and a return statement.

Although you can use lambda expressions to generate expression trees, there is nothing preventing you from directly creating your own expression tree. Let’s walk through an example of creating an expression tree for a lambda expression square = x => x * x.

C#
//example creates expression tree of x *x
    public static void CreatingExpressionTree()
    {
        ParameterExpression parameter1 = Expression.Parameter(typeof(int), "x");
        BinaryExpression multiply = Expression.Multiply(parameter1, parameter1);
        Expression<Func<int, int>> square = Expression.Lambda<Func<int, int>>(
            multiply, parameter1);
        Func<int, int> lambda = square.Compile();
        Console.WriteLine(lambda(5));
    }

You first start off with a parameter expression of type int.

C#
ParameterExpression parameter1 = Expression.Parameter(typeof(int), "x");

The next step is to build the body of lambda expressions which happens to be a binary expression. The body consists of a multiply operator to the same parameter expression.

C#
BinaryExpression multiply = Expression.Multiply(parameter1, parameter1);

The final step is to build the lambda expression which combines the body with the parameter as follows:

C#
Expression<Func<int, int>> square = Expression.Lambda<Func<int, int>>(multiply,
    parameter1);

The last step converts the expression to delegate and executes the delegate as follows:

C#
Func<int, int> lambda = square.Compile();
Console.WriteLine(lambda(5));

Creating An Expression from Another Expression

You can take an expression tree and modify it to create another expression from it. In the following example, we will start off with a lambda expression of x *x and then modify this expression to add 2 to it. Let’s have a look at an example:

C#
public static void CreatingAnExpressionFromAnotherExpression()
    {
        Expression<Func<int, int>> square = x => x * x;
        BinaryExpression squareplus2 = Expression.Add(square.Body,
            Expression.Constant(2));
        Expression<Func<int, int>> expr = Expression.Lambda<Func<int, int>>(squareplus2,
            square.Parameters);

        Func<int, int> compile = expr.Compile();
        Console.WriteLine(compile(10));
    }

We start off with a lambda expression which returns a square:

C#
Expression<Func<int, int>> square = x => x * x;

Next we generate the body of the new lambda expression by using the body of the first lambda expression and adding a constant of 2 to it and assigning it to the binary expression:

C#
BinaryExpression squareplus2 = Expression.Add(square.Body, Expression.Constant(2));

In the last step, we generate the new lambda expression by combining the body with the parameters from the first lambda expression. The important point which I discovered in the statement below is that, a parameter's reference needs to be exactly the same from the first lambda expression which is square.Parameters. You cannot create a new instance of parameters collection which results in a runtime error.

C#
Expression<Func<int, int>> expr = Expression.Lambda<Func<int, int>>(squareplus2,
    square.Parameters);

Closures and Lambda Expressions

Closure is a concept that comes from functional programming. It essentially captures or uses the variable which is outside the scope of the lambda expression. What it essentially means is you can use variables inside the lambda expression that are declared outside the scope of the lambda expression — you are able to use and capture the variable that is outside the scope of the lambda expression. This has its advantages but could lead to issues as well since the outside context has the ability to change the variable value. Let’s drill through an example of lambda expression in the context of closure.

C#
public static void LambdaWithClosure()
    {
        int mulitplyby = 2;
        Func<int, int> operation = x => x * mulitplyby;
        Console.WriteLine(operation(2));
    }

In the above example, we are using the mulitplyby variable inside the lambda expression although it is declared outside the scope of the expression. This concept is called variable capture. In the background, C# compiler takes all those captured variables and puts them in a generated class. When you are using lambda expressions with outside variables, they are not picked up by the garbage collector, and are forced to hang around until they are used by the lambda expressions and the expression goes out of scope.

There are certain restrictions when you are using a lambda expression with a parameter with ref and out keyword. When your variable is passed with ref or out keyword, you must explicitly specify the parameter type because the compiler cannot infer the type of the variable. As shown in the example below:

C#
delegate void OutParameter(out int i);
    delegate void RefParameter(ref int i);
    public static void GotchasWithLambdas()
    {
        //example with out parameter int i;
        OutParameter something = (out int x) => x = 5;
        something(out i);
        Console.WriteLine(i);

        //example with ref parameter.
        int a = 2;
        RefParameter test = (ref int x) => x++;
        test(ref a);
        Console.WriteLine(a);
    }

Notice in the above code, I am explicitly specifying the parameter type of int in both cases, ref and out. If I omit the parameter type, the compiler will raise an error.

Another restriction that I have come across using lambdas is you cannot use the params keyword in the parameter type for a lambda expression regardless of whether or not you explicitly specify the type of the parameter. The following code does not compile because the parameter definition is attributed with params keyword:

C#
delegate void ParmsParameter(params int[] ints);
    public static void LambdaWithParam()
    {
        ParmsParameter par = (params int[] ints) =>
        {
            foreach (int i in ints)
            {
                Console.WriteLine(i);
            }
        };
    }

Summary

In this article I introduced the syntax of a lambda expression — how it replaces anonymous methods. We also talked about how lambda expressions differ from anonymous methods because of the type inference, and its ability to be easily transformed into delegates or expression trees. We learned the parameter restrictions of lambda expressions and how to write an expression from scratch and compile it to a delegate and vice versa.

History

  • 10 March, 2008: Article posted
  • 12 March, 2008: Article updated

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Tarrant Regional Water District
United States United States
General Certificate of Education obtained in Math, Physics, Chemistry, Principle of Accounts & Statistics in the year 1998-99 from the University of Cambridge, London, England.
Earned a degree in Business Administration in Information System with a GPA of 3.6 on 4.0 scales from Univ. of Texas At Arlington in Honors in the year 2005.
Certified MCAD,MCSD,MCPD ,OCP (Oracle Certified Professional),MCDBA.

I have been working as an Asp.net C# developer for last 3 years.

Zeeshan Hirani
Senior Asp.net Developer
Tarrant Regional Water District
zhirani@trwd.com
http://www.trwd.com



Comments and Discussions

 
SuggestionNice and short about Lambda Expr. Pin
Syed Baqer Naqvi26-Jan-16 19:13
professionalSyed Baqer Naqvi26-Jan-16 19:13 
SuggestionSomething wrong Pin
sreekanth_ponneboina13-May-15 4:33
sreekanth_ponneboina13-May-15 4:33 
QuestionGreat Article Pin
Black Horus29-Aug-14 8:24
Black Horus29-Aug-14 8:24 
GeneralMy vote of 5 Pin
Member 812305029-Jul-14 12:44
Member 812305029-Jul-14 12:44 
GeneralReturn value of List<T>.FindAll Pin
John Ortiz9-Jun-14 10:29
John Ortiz9-Jun-14 10:29 
Generalnice Pin
Shahriar Hossain11-Oct-13 9:30
professionalShahriar Hossain11-Oct-13 9:30 
GeneralMy vote of 5 Pin
ioan.bucur12-Aug-13 21:15
ioan.bucur12-Aug-13 21:15 
Questiongood Pin
Archit Patel9-Apr-13 21:34
professionalArchit Patel9-Apr-13 21:34 
GeneralMy vote of 5 Pin
peru j2-Apr-13 0:42
peru j2-Apr-13 0:42 
GeneralMy vote of 5 Pin
Tom Clement18-Dec-12 18:27
professionalTom Clement18-Dec-12 18:27 
GeneralMy vote of 4 Pin
RenatoK5-Aug-12 12:39
RenatoK5-Aug-12 12:39 
GeneralMy vote of 5 Pin
Akram El Assas10-May-12 20:56
Akram El Assas10-May-12 20:56 
GeneralMy vote of 4 Pin
killabyte20-Feb-12 20:57
killabyte20-Feb-12 20:57 
GeneralMy vote of 2 Pin
Manuvdp11-Nov-09 21:57
Manuvdp11-Nov-09 21:57 
Generalbeware of reference variables in linq lambdas Pin
Member 11713655-Nov-09 18:59
Member 11713655-Nov-09 18:59 
GeneralParams Pin
Michal Brylka12-Mar-09 14:45
Michal Brylka12-Mar-09 14:45 
AnswerRe: Params Pin
paper133713-May-09 9:50
paper133713-May-09 9:50 
QuestionSearching for VB - Translation of 'LambdasNoParams' Pin
StSenff#15-Jul-08 7:26
StSenff#15-Jul-08 7:26 
GeneralExcellent article! Pin
Ravi Kallamadi8-Jun-08 17:22
Ravi Kallamadi8-Jun-08 17:22 
GeneralAwesome Pin
Karel Kral18-Mar-08 21:41
Karel Kral18-Mar-08 21:41 
Awesome article, thanks! I am using lambdas but thanks to your article I have found new perspective of using it.
General"Lamda" vs "Lambda" Pin
Michael Dorfman11-Mar-08 5:17
Michael Dorfman11-Mar-08 5:17 
GeneralRe: "Lamda" vs "Lambda" Pin
Zeeshan Jafar Hirani13-Mar-08 17:46
Zeeshan Jafar Hirani13-Mar-08 17:46 
GeneralGood article Pin
Priyank Bolia10-Mar-08 17:39
Priyank Bolia10-Mar-08 17:39 

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.