Thursday, July 9, 2009

Lambda Expressions – Features of C# 3.0 (Part – 6)

In my earlier article we have seen the following new features of C# 3.0.

In this blog we will try to understand the concept of Lambda expressions.

What is Lambda Expression?

Lambda expressions are nothing but anonymous functions. Lambda expressions can be used to write expressions or statements. To write Lambda expression one has to make use of the Lambda operator which is read as “goes to”. Lambda operator is represented as “=>” i.e. an equal to sign followed by a greater than symbol. Lambda expressions normally have input parameters on the left side of the Lambda operator and expressions/statements on the right side. The lambda operator is used to separate the input arguments on the left side from the expression/statements on the right side. Lambda expressions are extensively used in LINQ. An e.g. of Lambda expression is given below.

public class LambdaExpressionDemo
{
    delegate int Add(int a, int b);
    public void PrintAddResult(int num1, int num2)
    {
        Add addResult = (a, b) => a + b;
        Console.WriteLine(addResult(num1, num2).ToString());
    }

    public double CountEmp()
    {
         System.Collections.Generic.List<Person> perColl = new List<Person>{
             new Person {FName="first", LName="lastFirs", Age=30},
             new Person{FName="second", LName="lastSecond", Age=24},
             new Person{FName="third", LName="Third", Age=44}};
            double avgAge = perColl.Count(p => p.LName.StartsWith("la")); 
         return avgAge;
     }   
}

In the above code one can see we have declared a delegate which returns an integer value and takes two integers as input parameters. Also you can see I have declared a delegate variable (addResult) and assigned an anonymous function using our Lambda expression. One thing to note here is that there is no need to declare the type of the input arguments for the anonymous functions. The compiler will infer the type. What the above lambda expressions means is that we have an anonymous function with two input parameters (left side of the Lambda operators are input parameters) which needs to be added and the result returned. Don’t you thing this is a very simple way of declaring a function. In the second e.g. I have made use of Lambda expression along with Count method. The lambda expression evaluates and checks whether the person’ LName starts with “la” and returns true or false. If it returns true then that record is counted. So the count will be 2.

To better understand the advantages/usefullness of Lambda expression we need to know how we use to implement these things in the earlier versions of C#. Lets see how we would do this in C# 2.0 using anonymous methods.

delegate int Add(int a, int b);
public void PrintAddResult(int num1, int num2)
{
    //Using anonymous methods of C#2.0.
    Add addResult = delegate(int aa, int bb) { return aa + bb; };
    Console.WriteLine(addResult(num1, num2));
}

In the above e.g. we are making use of Anonymous method feature of C# 2.0 where we are making use of delegates to declare an Anonymous methods (highlighted in green). Wherever you thing creating a method is redundant or will create overhead you can go ahead and make use of Anonymous methods. Also one can use anonymous methods in cases where a method or property or constructor expects a delegate. In the above code we have declared a delegate with the signature of the method and then again using delegate variable, addResult, we are declaring an Anonymous method. One can write any number of statements inside Anonymous methods. The scope of the variables declared in Anonymous methods are within the limits of Anonymous methods.

Now lets see how we would have done this in the good olden days of C#1.0.

delegate int Add(int a, int b);
public void PrintAddResult(int num1, int num2)
{
    //The good olden days of C#1.0
    Add additionDel = Addition;
}

public int Addition(int a, int b)
{
    return a + b;
}

In the above code you can see the good olden days of delegate declaration where one has to create a method (Addition) and then create a delegate (Add) representing the method. Once you have declared the delegate one can assign the method. So after seeing the olden ways of declaring the same functionality which can be achieved using Lambda expression one can see the advantages.

One can define two types of lambda, an Expression Lambda and a Statement Lambda. Lets see what each one of it means.

Expression Lambda: Expression lambda is nothing but an expression on the right side of the Lambda Operator. E.g are given below.

(a, b) =>  (a + b);
(int a, int b) => a > b;

An expressions is any piece of code which can be evaluated to a single value, object etc. So the above code are just e.g. of expressions, even an assignment of a value to a variable is also an expression.

Statement Lambdas: As the name suggests statement lambdas are ones which can have more than one line of code/statement. In expression lambda you normally have only one line of statement which executes and produces the output in the form of a single value or object etc whereas in statement lambda we have more than one line of statements enclosed in a curly brace ({}).

delegate int Add(int a, int b);
public void StatementLambda(int operand1, int operan2)
{
    Add result = (a, b) =>
    {
        int c = a + b;
        Console.WriteLine(c.ToString());
        return c;
    };
    result(operand1, operan2);
}

In the above code you can see we have written some three lines of code and this is an e.g. of statement Lambda. There are no restriction to the number of lines of code that can be written in a Lambda expression. As a good practice it will be nice if you can restrict the number of lines between 2 and 3 lines.

Some points about Lambda expressions

  • Lambda expressions are anonymous functions.
  • No need to declare the types of the variables used in the input parameter of the Lambda expression. The types will be inferred by the compiler.
  • If there are situation when the compiler cannot infer the type of the variable used, in those kind of scenarios we can explicitly declare the types for the input parameters as shown below.

delegate int Add(int a, int b);
public void PrintAddResult(int num1, int num2)
{
    Add result = (int a, int b) => a + b;
    Console.WriteLine(result(num1, num2).ToString());
}

  • Variables declared in a Lambda expressions are scoped inside the Lambda blocks.
  • Lambda declaration should match the signature of the delegate i.e. the number of parameters and return type.
  • Parameter less Lambdas can also be declared just by having a empty parentheses as shown below.

delegate void Print();
public void ParameterLessLambda()
{
    Print print = () => Console.WriteLine("Parameter less lambda executed.");
    print();
}

  • You can pass Lambda expression where a delegate is expected. E.g. is shown below.

/*Lambda expression passed as an argument where delegate was expected.*/
System.Threading.Thread thh = new System.Threading.Thread(
   () => Console.WriteLine("hi"));

  • When you are making use of Lambda expressions behind the scene the compiler does the extra work of creating an anonymous function for the Lambda expression. Lets see with an e.g.: the code and generated IL are pasted below.

/*Class having a delegate and a Lambda expression.*/
class LambdaExpression
{
    delegate int Add(int operand1, int operand2);
    public void LambdaExp(int operan1, int operan2)
    {
        //Lambda expression.
        Add result = (a, b) => a + b;
        Console.WriteLine(result(operan1, operan2));
    }
}

/*Below is the MSIL generated for the above method. */
.method public hidebysig instance void LambdaExp(int32 operan1,
int32 operan2) cil managed
{
    .maxstack 3
    .locals init (
        [0] class
LambdaExpressions.LambdaExpression/Add result)
    L_0000: nop
    L_0001: ldsfld class
LambdaExpressions.LambdaExpression/Add
LambdaExpressions.LambdaExpression::CS$<>9__Cach
edAnonymousMethodDelegate1
    L_0006: brtrue.s L_001b
    L_0008: ldnull
    L_0009: ldftn int32
LambdaExpressions.LambdaExpression::<LambdaExp>b
__0(int32, int32)
    L_000f: newobj instance void
LambdaExpressions.LambdaExpression/Add::.ctor
(object, native int)
    L_0014: stsfld class LambdaExpressions.LambdaExpression/Add
LambdaExpressions.LambdaExpression::CS$<>9__Cach
edAnonymousMethodDelegate1
    L_0019: br.s L_001b
    L_001b: ldsfld class
LambdaExpressions.LambdaExpression/Add
LambdaExpressions.LambdaExpression::CS$<>9__Cach
edAnonymousMethodDelegate1
    L_0020: stloc.0
    L_0021: ldloc.0
    L_0022: ldarg.1
    L_0023: ldarg.2
    L_0024: callvirt instance int32
LambdaExpressions.LambdaExpression/Add::Invoke(i
nt32, int32)
    L_0029: call void [mscorlib]System.Console::WriteLine(int32)
    L_002e: nop
    L_002f: ret
}

/*C# code decompiled from MSIL using .NET Reflector*/
internal class LambdaExpression
{
    // Methods
    public void LambdaExp(int operan1, int operan2)
    {
        Add result = delegate (int a, int b) {
            return a + b;
        };
        Console.WriteLine(result(operan1, operan2));
    }

    // Nested Types
    private delegate int Add(int operand1, int operand2);
}

In the above pasted code you can see the MSIL generated is making use of AnonymousMethodDelegate i.e. the compiler is converting Lambda expressions into Anonymous functions. This is proven from the decompiled C# code from the MSIL pasted above.

As with other features of C# 3.0 Lambda expression is also a way of writing terse code where the compiler does the extra work of generating the extra codes. In my next  we will have a look at Language Integrated Query (LINQ) for which all these features were incorporated in C#. Till then write terse code and try to learn more.

Sandeep

2 comments:

  1. I wrote a similar article describing lambda expressions for beginners.

    ReplyDelete
  2. Trade FX At Home On Your PC: tradeatf Is A Forex Trading Company. The Company States That You Can Make On Average 80 – 300 Pips Per Trade. tradeatf States That It Is Simple And Easy To Get Started.

    ReplyDelete

Please provide your valuable comments.