Understanding Lambda Expressions

Abstract

The concept of functional programming was introduced in .NET 3.0 in the form of lambda expressions where we can succinctly define function objects for use at any time. However, C# endorsed this capability via delegates earlier, whereby you create a function object. Lambda expressions provide a more natural way to create and invoke a function. Lambda expressions are a syntax whereby you can declare anonymous functions (delegates) by joining two actions simultaneously, their creation and a connection, into one expressive statement in the code.

Lambda Expressions

Lambda expressions are unnamed and are defined using the lambda operator (=>). They are very similar to anonymous methods, however more flexible and encapsulates code without the normal function keyword signature. The left-hand side of the lambda operator is typically, the input and right-hand side points to code to be executed. Lambda expressions are useful, while LINQ queries, less verbose, eliminate the need for anonymous methods where the delegate type is typically employed in C# code to achieve a specific feat. The following code snippet adds two numerics using delegate types.

Listing 1: Lambda expression sample code (calculating addition)

  1. class Program  
  2. {  
  3.         // delegates  
  4.         delegate int test(int x, int y);  
  5.   
  6.         static void Main(string[] args)  
  7.         {  
  8.             // Lambda expression  
  9.             test mthd = (x, y) => { return x + y; };  
  10.   
  11.             //method calling with parameters  
  12.             Console.WriteLine("Addition is {0}",mthd(20,30));  
  13.   
  14.             Console.ReadKey();    
  15.         }  
  16. }  
Apart from that, if the lambda expression has only a single statement and that statement sets the return value, we can omit the curly braces and the return statements. Here, a defined lambda expression accepts two integer type values and returns the addition of those values as in the following.

Listing 2: Lambda expressions without curly braces
  1. // delegates  
  2. delegate int test(int x);  
  3.   
  4. static void Main(string[] args)  
  5. {  
  6.    // Lambda expression  
  7.    test mthd = x => x * x;   
  8.   
  9.    //method calling with parameters  
  10.    Console.WriteLine("Square is {0}",mthd(20));  
  11. }  
Moreover, within the body of the lambda expressions, we can also refer to a local variable, properties and data members that are within the scope at that time. The following code shows the uses of a local variable.

Listing 3: Lambda expressions with local variable reference in the scope
  1. class Program  
  2. {  
  3.         // delegates  
  4.         delegate double CalArea(double r);  
  5.   
  6.         static void Main(string[] args)  
  7.         {  
  8.             double pi = 3.14;  
  9.             // Lambda expression  
  10.             CalArea mthd = r => pi * r * r;   
  11.   
  12.             //method calling with parameters  
  13.             Console.WriteLine("\n\nArea is = {0}",mthd(7));  
  14.             Console.ReadKey();    
  15.         }  
  16. }  
The previous code shall yield the calculated area as an output over a console based application, after being successfully compiled.

Area Calculation Output
                                                            Figure 1: Area Calculation Output

Lambda expressions are nothing more than a very exact way to write anonymous methods and ultimately simplify how we work especially, with the, .NET delegate's types. The following code uses lambda expressions to simplify the call to FindAll even more. When we make use of lambda syntax, there is no trace of underlying delegate objects.

Listing 4: Data searching using lambda expressions
  1. class Program  
  2. {  
  3.         static void Main(string[] args)  
  4.         {  
  5.             searchData();  
  6.             Console.ReadKey();    
  7.         }  
  8.   
  9.         static void searchData()  
  10.         {  
  11.             List<int> lst = new List<int>();  
  12.             lst.AddRange(new int[] {21,22,18,10,35,42,23,24,25});  
  13.   
  14.             List<int> even = lst.FindAll(i => (i % 2)== 0);  
  15.   
  16.             Console.WriteLine("\nEven Number Listing\n");  
  17.   
  18.             foreach(int evNumber in even)  
  19.             {  
  20.                 Console.WriteLine("{0}\t", evNumber);  
  21.             }  
  22.   
  23.         }  
  24. }  
In this iteration, there is no trace of the delegate and the calculation is done merely using a lambda expression.

Even Numbers listing
                                                               Figure 2: Even Numbers listing

A lambda expression can be used anywhere you would have used an anonymous method or delegate type. The C# compiler translates the expression into a standard anonymous method making use of the delegate type implicitly that can be verified using ILDASM.EXE as in the following:

predicate in IL code
                                                            Figure 3: predicate<> in IL code

More specifically, examine our previous code in which a lambda expression was used as:
  1. List<int> even = lst.FindAll(i => (i % 2)== 0);  
As a rule of thumb, a lambda expression is ultimately converted into its delegate counterpart code as in the following:
  1. List<int> even = lst.FindAll(delegate (int i));   
Summary

This chapter has introduced the theory behind lambda expressions as well as illustrates its syntax of expressions. We have been provided with an understanding of how lambda expressions are replacements for anonymous methods. We have also seen how to convert lambda expressions, with and without statement bodies, into delegates.