in Development

C# 3.0 – Lambda Expressions

"A lambda is an inline expression or statement block with a concise syntax that can be used in C# 3.0 and later wherever a delegate type is expected" . In a certain way we can consider Lambda Expressions as an evolution of the anonymous methods.

Look the examples below, at first sight the syntax looks very weird but when you know how they are built and you get used, you will see that it’s extremely easy.

x => x + 1

x => { return x + 1; }

(x, y) => x == y

(int x, int y) => x * y

() => ExecuteAnyMethod();

Maybe you already noticed that a lambda consist of a list of input parameters, the operator "=>" (goes to) and a expression or statement. In the form of:

(input parameters) => Expression or Statement.

The list of parameters is optional, and you don’t need to specify the types of the parameters in the cases where the type can be inferred. Parenthesis are also optional when we only specify one parameter.

After this fast overview the best is to see some code to understand better where Lambdas can be used.

   1: static void Main(string[] args)
   2: {
   3:     int[] numbers = { 1, 2, 3, 4, 5 };
   4:  
   5:     Filter filter = new Filter(IsEven);
   6:     // Filter filter = new Filter(IsOdd);
   7:  
   8:     PrintNumbers(numbers, filter);
   9:  
  10:     System.Console.ReadKey();
  11: }
  12:  
  13: static void PrintNumbers(int[] numbers, Filter filter)
  14: {
  15:     foreach (int number in numbers)
  16:     {
  17:         if (filter(number))
  18:             System.Console.WriteLine(number.ToString());
  19:     }
  20: }
  21:  
  22: static bool IsEven(int number)
  23: {
  24:     return number % 2 == 0;
  25: }
  26:  
  27: static bool IsOdd(int number)
  28: {
  29:     return !IsEven(number);
  30: }

The code is really simple, we want to print on the screen some numbers based on a filter condition. The filter condition "IsEven" is set through a delegate of type "Filter", that receives an int and returns true if ithe number satisfies the condition. With this kind of construction, we can easily switch which numbers must be printed without modifying the main business logic of PrintNumbers. As you can see in the code it would be as easy as replace line 7 by the line 6.

In C# 2.0 the code can be adjusted using anonymous method, so the filter condition is passed inline to PrintNumbers.

   1: public delegate bool Filter(int number);
   2:  
   3: static void Main(string[] args)
   4: {
   5:     int[] numbers = { 1, 2, 3, 4, 5 };
   6:  
   7:     PrintNumbers(numbers, delegate(int number) { return number % 2 == 0; });
   8:  
   9:     System.Console.ReadKey();
  10: }
  11:  
  12: static void PrintNumbers(int[] numbers, Filter filter)
  13: {
  14:     foreach (int number in numbers)
  15:     {
  16:         if (filter(number))
  17:             System.Console.WriteLine(number.ToString());
  18:     }
  19: }

With the anonymous method we don’t need to declare the method IsEven and IsOdd anymore, we can just pass to PrintNumbers the condition to apply inline, look line 8.

Lambda expression improve anonymous methods providing a more concise syntax. Look how we can do the same with C# 3.0.

   1: static void Main(string[] args)
   2: {
   3:     int[] numbers = { 1, 2, 3, 4, 5 };
   4:  
   5:     PrintNumbers(numbers, number => number % 2 == 0);
   6:  
   7:     System.Console.ReadKey();
   8: }
   9:  
  10: static void PrintNumbers(int[] numbers, Funcint, bool> filter)
  11: {
  12:     foreach (int number in numbers)
  13:     {
  14:         if (filter(number))
  15:             System.Console.WriteLine(number.ToString());
  16:     }
  17: }

Look the line 5, we are still doing the same, but we don’t need anymore the weird syntax of anonymous methods and the type int of parameter "number" is directly inferred. In addition note that we don’t declare our custom Filter delegate anymore, but we use a delegate called Func. C# 3.0 introduces several default delegates that allow you to encapsulate methods which have from 0 to 4 parameters and return a value. They parameters and results types are generic, so with these delegates we cover a big percentage of the methods we normally use in our code.

Is this cool or what?