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()
.
public static int Addition(int no1, int no2)
{
return no1 + no2;
}
Delegate
declaration for this method would look like:
public delegate int MyDelegate(int no1,int no2);
Note that delegate
declaration should match with signature of method. (As delegate
s 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.
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:
int r = delObj.Invoke(2,5);
or:
int r = delObj(2,5);
In the second case, the compiler will convert it as delObj.Invoke(2,5);
Complete Code
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:
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:
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.
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:
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
Delegate
s 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:
public static int Addition(int no1, int no2)
{
return no1 + no2;
}
Delegate declaration
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.
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.)
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:
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:
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:
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:
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:
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
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.
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
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:
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:
- Lambda expression is anonymous method i.e compiler will create new method for lambda expression (that too on separate thread).
- 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.
- 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:
- anonymous class for lambda expression. (Closure Class)
- class variables to save values of outer variables used in lambda expression. (Closure variable)
- 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.
- anonymous method for lambda expression itself.
In above case compiler will create Anonymous closure class as follow:
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