Click here to Skip to main content
15,860,859 members
Articles / Programming Languages / C#

Everything That You Need to Understand about Lambda Expressions

Rate me:
Please Sign up or sign in to vote.
4.84/5 (66 votes)
15 Jan 2015CPOL9 min read 54.8K   90   28
This write-up will take you through all the steps that are required to understand lambda expression thoroughly.

Introduction

Lambda expression is just another way to write anonymous method. To understand lambda expression, one should first understand concept of delegates and anonymous methods. If you are well versed with these concepts, then you can directly go to the lambda expression section.

We will be elaborating concepts in the following sequence:

  • Delegates
  • Anonymous Method
  • Lambda Expression
  • Func<> Delegate
  • Delegates in Action
  • Lambda Expressions in Action
  • Lambda Expression - Real Life Example
  • Closure

Delegates

Delegates are type safe reference to methods. In other words, you can hold address of method in delegate object.

For example, if you have a method Addition().

C#
public static int Addition(int no1, int no2)
{
      return no1 + no2;
}

Delegate declaration for this method would look like:

C#
public delegate int MyDelegate(int no1,int no2);

Note that delegate declaration should match with signature of method. (As delegates are type safe references to methods).

Now you can create object of the above delegate type. While creating delegate object, we need to specify name of method to be referred in constructor.

C#
MyDelegate delObj = new MyDelegate(Addition); 

(Note that it's similar to class and object. We first write class and then can create multiple objects of that class. In the same way, we are first declaring delegate type and then creating delegate object from it. In case of delegate, it's a little confusing as delegate declaration and delegate object both are referred to as delegate.)

Once you have reference of “Addition” method in delObj, you can make call as:

C#
int r = delObj.Invoke(2,5);

or:

C#
int r = delObj(2,5);

In the second case, the compiler will convert it as delObj.Invoke(2,5);

Complete Code

C#
class Program
{
    public delegate int MyDelegate(int no1,int no2);

    static void Main(string[] args)
    {
        MyDelegate delObj = new MyDelegate(Addition);
        int r = delObj(2,5);
        Console.WriteLine("Result = " + r);

        Console.ReadKey();
    }

    public static int Addition(int no1, int no2)
    {
        return no1 + no2;
    }
}

Anonymous Method

Anonymous methods are methods without name. We can write small method block without any name and assign its address to delegate object, then this delegate object is used to call it.

The above example can be done using anonymous method as follows:

C#
class Program
{
    public delegate int MyDelegate(int no1,int no2);

    static void Main(string[] args)
    {
        MyDelegate delObj = delegate(int no1, int no2)
                            {
                                return no1 + no2;
                            };

        int r = delObj(2,5);
        Console.WriteLine("Result = " + r);

        Console.ReadKey();
    }
}

Here, we are using delegate keyword to create anonymous method. This method is the same as Addition() method in the first example, but it doesn’t have any name. While creating this method, we are assigning its address to delegate object delObj. So we can call this anonymous method using delObj delegate object (call is same as in the previous example).

Lambda Expression

Lambda expression is just another way to write anonymous method. The above code can be re-written using lambda expression as:

C#
class Program
{
    public delegate int MyDelegate(int no1,int no2);

    static void Main(string[] args)
    {
        MyDelegate delObj = (no1, no2) => no1 + no2;
        int r = delObj(2,5);
        Console.WriteLine("Result = " + r);

        Console.ReadKey();
    }
}

Points to Note

  • Lambda expression comprises parameter list and method body as anonymous method. What there before => operator is parameter list enclosed in brackets, and after => operator is method body.
  • No need to use return keyword, compiler will put it for you.
  • No need of delegate keyword
  • Data-types are not required for parameters. Parameters will get type information from delegate declaration (MyDelegate).

If lambda expression has only one parameter, then even parenthesis around parameters is not required.

For example, the following is a lambda expression to calculate square of a given number.

C#
class Program
{
    public delegate int MyDelegate(int no);

    static void Main(string[] args)
    {
        MyDelegate delObj = no => no * no;
        int r = delObj(4);
        Console.WriteLine("Result = " + r);

        Console.ReadKey();
    }
}

Func<> Delegate

Func<> is a predefined generic delegate in framework that we can use to hold reference of method. It has multiple overloads we can use one depending on signature of method that we want to refer.

In this example, we are going to use Func<T, TResult> overload. It can encapsulate method with one parameter and a return value.

T: Type of parameter.
TResult: Type of return type.

Same example can be written using Func<T, TResult> as:

C#
class Program
{
    static void Main(string[] args)
    {
        Func<int, int> delObj = no => no * no;
        int r = delObj(4);
        Console.WriteLine("Result = " + r);

        Console.ReadKey();
    }
}

In the above code, we need not declare delegate (like MyDelegate) explicitly. Instead we are using Func<T, TReturn> delegate. This delegate simplifies code and eliminates the need to declare delegate.

Delegates in Action

Delegates are used when we want to pass method as a parameter to other method. For example, passing method fun1() to method fun2() as a parameter.

To understand it better, let us revisit our first example.

We have one Addition method:

C#
public static int Addition(int no1, int no2)
{
    return no1 + no2;
}

Delegate declaration

C#
public delegate int MyDelegate(int no1, int no2);

Main method: This time, we are not calling Addition() method directly from Main. Examine the code and read Points to Note carefully.

C#
static void Main(string[] args)
{
    MyDelegate delObj = new MyDelegate(Addition);
    int r = Calculate(delObj);
    Console.WriteLine("Result = " + r);

    Console.ReadKey();
}
public static int Calculate(MyDelegate delObj)
{
    int r = delObj(2, 5);
    return r;
}

Points to Note

  • First, we are saving reference/address of Addition method in delObj
  • From Main, we are making a call to Calculate() method. But while calling it, we are passing address of Addition() method (i.e., delObj) to Calculate() method as parameter. (Addition(): call back method)
  • Now in Calculate method, we are making call to delObj delegate. As delObj is holding address of Addition() method, it will call the same.
  • You can replace code in Main with the following code. (There is no need to create delegate object. You can directly specify method name in Calculate() method.)
C#
static void Main(string[] args)
{
   int r = Calculate(Addition);
   Console.WriteLine("Result = " + r);

   Console.ReadKey();
}

There must be one big question in your mind that if we want to make call to Addition method from Calculate() method, why not write something like this:

C#
class Program
{
    public delegate int MyDelegate(int no1, int no2);

    static void Main(string[] args)
    {
        int r = Calculate();
        Console.WriteLine("Result = " + r);

        Console.ReadKey();
    }

    public static int Calculate()
    {
        int r = Addition(2,5);
        return r;
    }

    public static int Addition(int no1, int no2)
    {
        return no1 + no2;
    }
}

But this way, you will hard-code call to Addition() in Calculate() method. What if we have want to do subtraction and this time we want to call Subtraction() method instead of Addition(). To make this possible, we have to use delegate approach.

The following example will make it very clear:

C#
class Program
{
    public delegate int MyDelegate(int no1, int no2);

    static void Main(string[] args)
    {
        int r = Calculate(Addition);
        Console.WriteLine("Result = " + r);

        r = Calculate(Substraction);
        Console.WriteLine("Result = " + r);

        Console.ReadKey();
    }

    public static int Calculate(MyDelegate delObj)
    {
        int r = delObj(2, 5);
        return r;
    }

    public static int Addition(int no1, int no2)
    {
        return no1 + no2;
    }

    public static int Substraction(int no1, int no2)
    {
        return no1 - no2;
    }
}
  • Here, we are adding one more method Substraction().
  • Calculate method doesn’t hardcode call to Addition() method. It calls using delegate.
  • We can pass address of any method to Calculate(). As in Line No 3, we are passing address of Subtraction() method to it.
  • These methods are generally referred to as callback methods.

Lambda Expressions in Action

In the above scenario, whenever we want to add new functionality for Calculate() method, we need to first write a new method and then we need to pass its reference to Calculate() method.

For example, to support multiplication, we first have to write Multiplication() method and then we need to pass its reference to Calculate() method. This approach is good if your method is complex enough to have multiple lines of code. But for one line methods like Addition() and Substaction(), instead of creating separate method, can we not write it as anonymous method? And as we know, lambda expression provides an elegant way to write anonymous methods. So, in the above code, we can refactor using lambda expression as follows:

C#
class Program
{
    public delegate int MyDelegate(int no1, int no2);

    static void Main(string[] args)
    {
        int r = Calculate((no1,no2) => no1 + no2);
        Console.WriteLine("Result = " + r);

        r = Calculate((no1, no2) => no1 - no2);
        Console.WriteLine("Result = " + r);

        Console.ReadKey();
    }

    public static int Calculate(MyDelegate delObj)
    {
        int r = delObj(2, 5);
        return r;
    }
}

Points to Note

  • Here, while calling calculate method, we are directly writing body of Addition() and Substraction() in lambda expression syntax.
  • Anonymous method will get created in memory and its address will get passed to Calculate() method. Calculate() method will hold that address in delObj delegate object. And will call the same using it.
  • Now if you want support for multiplication functionality; one line of code will do:
C#
r = Calculate((no1, no2) => no1 * no2);

Lambda Expression - Real Life Example

The example used above may not be useful in real life scenario, but I have used it just to explain concept in its simplest form. In real life, lambda expressions are extensively used while writing LINQ queries.

The following example will give you a little test of real life usage of lambda expression:

C#
public class Employee
    {
        public int EmpId { get; set; }
        public string EmpName { get; set; }
        public double Salary { get; set; }
        public string City { get; set; }

        public override string ToString()
        {
            return String.Format("{0} {1} {2} {3}",EmpId,EmpName,Salary,City);
        }
    }
    
    public class Program
    {
        static void Main(string[] args)
        {
            List<Employee> employees = new List<Employee>();
            employees.Add(new Employee 
            { EmpId = 1, EmpName = "Mikyway", Salary = 10000, City = "Mumbai" });
            employees.Add(new Employee 
            { EmpId = 2, EmpName = "Andromeda", Salary = 20000, City = "Newyork" });
            employees.Add(new Employee 
            { EmpId = 3, EmpName = "Sculptor", Salary = 30000, City = "Mumbai" });

            foreach(Employee emp in FilterByCity(employees, "Mumbai"))
            {
                Console.WriteLine(emp.ToString());
            }

            Console.ReadKey();
        }

        public static IEnumerable<Employee> FilterByCity
            (IEnumerable<Employee> employees, string filterStr)
        {
            foreach(Employee emp in employees)
            {
                if (emp.City == filterStr)
                    yield return emp;
            }
        }
    }

In the above code:

  • We have Employee class with few properties
  • List employees is collection of Employee
  • Filter is a method which filters list of employees based on City

What if we want to filter list based on any other field, i.e., Name or Salary. Then instead of writing specific FilterByCity() method, we should write generic Filter() method which should able to filter using any field.

This is possible if we can take out hard coded condition if (emp.City == filterStr) out of FiterByCity() method. And we can do it using lambda expression as follows:

Generic Filter() using lambda expression

C#
public delegate bool EmpDelegate(Employee emp);

public class Program
{
    static void Main(string[] args)
    {
        ...

        Console.WriteLine("Filter by City");
        foreach(Employee emp in Filter(employees, emp => emp.City == "Mumbai"))
        {
            Console.WriteLine(emp.ToString());
        }

        Console.WriteLine("\nFilter by Salary");
        foreach (Employee emp in Filter(employees, emp => emp.Salary >= 20000))
        {
            Console.WriteLine(emp.ToString());
        }

        Console.ReadKey();
    }

    public static IEnumerable<Employee> Filter(IEnumerable<Employee> employees, EmpDelegate delObj)
    {
        foreach(Employee emp in employees)
        {
            if (delObj(emp) == true)
                yield return emp;
        }
    }
}

The same thing can be done using builtin Func<> delegate. In this case, explicit delegate declaration (EmpDelegate) is not required.

C#
public static IEnumerable<Employee> Filter(IEnumerable<Employee> employees, Func<Employee,bool> delObj)
{
    foreach(Employee emp in employees)
    {
        if (delObj(emp) == true)
            yield return emp;
    }
}

Complete Code

C#
public class Employee
    {
        public int EmpId { get; set; }
        public string EmpName { get; set; }
        public double Salary { get; set; }
        public string City { get; set; }

        public override string ToString()
        {
            return String.Format("{0} {1} {2} {3}",EmpId,EmpName,Salary,City);
        }
    }
    
    public class Program
    {
        static void Main(string[] args)
        {
            List<Employee> employees = new List<Employee>();
            employees.Add(new Employee 
            { EmpId = 1, EmpName = "Mikyway", Salary = 10000, City = "Mumbai" });
            employees.Add(new Employee 
            { EmpId = 2, EmpName = "Andromeda", Salary = 20000, City = "Newyork" });
            employees.Add(new Employee 
            { EmpId = 3, EmpName = "Sculptor", Salary = 30000, City = "Mumbai" });

            Console.WriteLine("Filter by City");
            foreach(Employee emp in Filter(employees, emp => emp.City == "Mumbai"))
            {
                Console.WriteLine(emp.ToString());
            }

            Console.WriteLine("\nFilter by Salary");
            foreach (Employee emp in Filter(employees, emp => emp.Salary >= 20000))
            {
                Console.WriteLine(emp.ToString());
            }

            Console.ReadKey();
        }

        public static IEnumerable<Employee> 
        Filter(IEnumerable<Employee> employees, Func<Employee,bool> delObj)
        {
            foreach(Employee emp in employees)
            {
                if (delObj(emp) == true)
                    yield return emp;
            }
        }
    }  

I hope this example will help you to understand LINQ queries better. To keep this example as simple as possible and to focus only on lambda expression, few industry standards are avoided like containment, extension method, etc.

Closure:

I'm including this section in response to suggestion in comments. I hope this will make it more useful.

First examine the following code:

C#
static void Main(string[] args)
{
    int i = 5;
    Func<int, int> add = x => x + i;

    Console.WriteLine(add(10));
    Console.ReadKey();
}

Here we have lambda expression "add" with one parameter. We are passing 10 to parameter x. And variable i is already initialized to 5. If you will run this code output will be 15. quite obvious.

Well after reading block of code if you are getting intuition of disaster!!!... Then congratulations! you understood lambda expressions well. And if above code is quit obvious for you and you can't figure out anything strange over there, then sorry to say but you need to read article once again....

Okey don't worry just go through the following points and then read above block of code:

 

  1. Lambda expression is anonymous method i.e compiler will create new method for lambda expression (that too on separate thread).
  2. In above example i is a local variable of function Main(). If you remember your initial programming lessons we learn that "scope and lifetime of local variable of function is within function only" i.e. we can't access local variable of function outside the function.
  3. In above code lambda expression is accessing variable i of Main() (without any error!!!).

So we can say that with lambda expressions we can access variables outside the block of the lambda expression. And This concept is known as Closure.

Million dollar question: Is the concept of closure violates the fundamental laws of programming? Not actually. To understand concept let us see what happening under the hood. Whenever we use outer variable in lambda expression compiler creates:

  1. anonymous class for lambda expression. (Closure Class)
  2. class variables to save values of outer variables used in lambda expression. (Closure variable)
  3. constructor to pass outer variable values that we used in lambda expression. i.e. If in lambda expression we will use two outer variables, compiler will create constructor with two parameters.
  4. anonymous method for lambda expression itself.

In above case compiler will create Anonymous closure class as follow:

C#
public class AnonymousClosureClass
{
    private int i;
    public AnonymousClosureClass(int i)
    {
        this.i = i;
    }
    public int AnonymousMethod(int x)
    {
        return x + i;
    }
}

In fact variable i of Main() function is not accessible to lambda expression. But its value is getting passed to constructor and then constructor is assigning that value to class variable i (closure variable). Also note that variable i that anonymous method is using is class variable and not a local variable i of Main() function. In short no need to throw out your old programming books :)

References:

  • Apress Pro ASP.NET MVC 5
  • Wrox Professional C# 2012 and .NET 4.5
  • http://msdn.microsoft.com/en-us/magazine/cc163362.aspx
  • http://msdn.microsoft.com/en-us/library/orm-9780596516109-03-09.aspx

License

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


Written By
India India
13+ Years of Experience. Seen death and rise of various technologies. Started with VB - 6.0 back in 2004, then updated to .NET somewhere in 2008. used Win Forms, ASP.NET Web Forms. Switched to ASP.NET MVC in 2013 for very short time. Right now using ASP Core since 2015 when it was in RC 1.

Comments and Discussions

 
GeneralGood Explanation Pin
Alireza_13623-Apr-16 16:27
Alireza_13623-Apr-16 16:27 
GeneralA great starting point for sure Pin
GeekyMummy6-Mar-15 4:37
GeekyMummy6-Mar-15 4:37 
GeneralMy vote of 5 Pin
Franc Morales15-Feb-15 23:31
Franc Morales15-Feb-15 23:31 
SuggestionA little Suggestion Pin
najunuoyan13-Feb-15 19:06
najunuoyan13-Feb-15 19:06 
GeneralMy vote of 5 Pin
Mahsa Hassankashi7-Feb-15 23:24
Mahsa Hassankashi7-Feb-15 23:24 
QuestionAs others have stated Pin
Sacha Barber23-Jan-15 0:24
Sacha Barber23-Jan-15 0:24 
GeneralMy vote of 3 Pin
Arun Babu A22-Jan-15 22:24
Arun Babu A22-Jan-15 22:24 
QuestionThis is far from everything you need to know about Lambdas Pin
DaveBlack19-Jan-15 11:14
DaveBlack19-Jan-15 11:14 
AnswerRe: This is far from everything you need to know about Lambdas Pin
Bhushan Mulmule19-Jan-15 17:33
Bhushan Mulmule19-Jan-15 17:33 
GeneralRe: This is far from everything you need to know about Lambdas Pin
DaveBlack19-Jan-15 18:23
DaveBlack19-Jan-15 18:23 
GeneralRe: This is far from everything you need to know about Lambdas Pin
Andreas Gieriet20-Jan-15 2:40
professionalAndreas Gieriet20-Jan-15 2:40 
GeneralRe: This is far from everything you need to know about Lambdas Pin
Bhushan Mulmule21-Jan-15 4:13
Bhushan Mulmule21-Jan-15 4:13 
GeneralMy vote of 5 Pin
CeGu19-Jan-15 1:38
professionalCeGu19-Jan-15 1:38 
Questionmissing crucial concept of anonymous delegates and lambda expressions: closure Pin
Andreas Gieriet18-Jan-15 2:50
professionalAndreas Gieriet18-Jan-15 2:50 
QuestionI enjoyed reading your article Pin
Alan Streisel17-Jan-15 5:32
Alan Streisel17-Jan-15 5:32 
AnswerRe: I enjoyed reading your article Pin
Bhushan Mulmule17-Jan-15 22:17
Bhushan Mulmule17-Jan-15 22:17 
GeneralMy vote of 3 Pin
pravin prajapati16-Jan-15 21:30
professionalpravin prajapati16-Jan-15 21:30 
QuestionSorry total lost with the final real life example Pin
Bob100016-Jan-15 9:42
professionalBob100016-Jan-15 9:42 
AnswerRe: Sorry total lost with the final real life example Pin
Bhushan Mulmule17-Jan-15 22:16
Bhushan Mulmule17-Jan-15 22:16 
QuestionLambda Expressions - Reals Live Example section needs to be revised Pin
Bluestater16-Jan-15 8:04
Bluestater16-Jan-15 8:04 
AnswerRe: Lambda Expressions - Reals Live Example section needs to be revised Pin
Bhushan Mulmule17-Jan-15 22:15
Bhushan Mulmule17-Jan-15 22:15 
QuestionIdentical code examples at end? Pin
Paul_Williams16-Jan-15 6:38
Paul_Williams16-Jan-15 6:38 
AnswerRe: Identical code examples at end? Pin
Bhushan Mulmule17-Jan-15 22:04
Bhushan Mulmule17-Jan-15 22:04 
QuestionNice Article Pin
rehmanabdurr16-Jan-15 0:14
rehmanabdurr16-Jan-15 0:14 
GeneralMy vote of 5 Pin
Humayun Kabir Mamun15-Jan-15 18:03
Humayun Kabir Mamun15-Jan-15 18:03 

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.