Lambda

Lambda expressions

Sometimes the whole signature of a method can be more code than the body of a method. There are also situations in which you need to create an entire method only to use it in a delegate.

For these cases, Microsoft added some new features to C#, 2.0 anonymous methods were added. In C# 3.0, things became even better when lambda expressions were added. Lambda expression is the preferred way to go when writing new code.

Introduction to Lambda Expressions1

Below is an example of newer lambda syntax.

 1internal class Program
 2{
 3    public delegate double MathDelegate(double value1, double value2);
 4    static void Main(string[] args)
 5    {
 6        MathDelegate mathDelegate = (x, y) => x + y;
 7        var result = mathDelegate(5, 2);
 8        Console.WriteLine(result);
 9        // output: 7
10        mathDelegate = (x, y) => x - y;
11        result = mathDelegate(5, 2);
12        Console.WriteLine(result);
13        // output: 3
14        Console.ReadLine();
15    }
16}

When reading this code, you can say “go or goes” to for the special lambda syntax. For example, the first lambda expression in the above example is read as “x and y goes to adding x and y”.

The lambda function has no specific name as the methods. Because of this, lambda functions are called anonymous functions. You also don’t have to specify a return type explicitly. The compiler infers this automatically from your lambda. And in the case of the above example, the types of parameters x and y are also not specified explicitly.

You can create lambdas that span multiple statements. You can do this by adding curly braces around the statements that form the lambda as below example shows.

1MathDelegate mathDelegate = (x, y) =>
2{
3    Console.WriteLine("Add");
4    return x + y;
5};

You can learn more about .NET built-in delegates here.

Lambda examples

Si riportano di seguito alcuni esempi che spiegano il passaggio da funzione anonima a lambda expression

Si consideri il seguente esempio di delegato con funzione anonima:

 1public class Student
 2{
 3    public int Id { get; set; }
 4    public string? Name { get; set; }
 5    public int Age { get; set; }
 6}
 7internal class Program
 8{
 9    delegate bool IsTeenAger(Student stud);
10    static void Main(string[] args)
11    {
12        IsTeenAger isTeenAger = delegate (Student s)
13        {
14            return s.Age > 12 &&
15        s.Age < 20;
16        };
17        Student stud = new Student() { Age = 25 };
18        Console.WriteLine(isTeenAger(stud));
19        Console.ReadLine();
20    }
21}

Lo stesso risultato si può ottenere con il seguente costrutto lambda

 1public class Student
 2{
 3    public int Id { get; set; }
 4    public string? Name { get; set; }
 5    public int Age { get; set; }
 6}
 7internal class Program
 8{
 9    delegate bool IsTeenAger(Student stud);
10    static void Main(string[] args)
11    {
12        IsTeenAger isTeenAger = s =>  s.Age > 12 && s.Age<20;
13        Student stud = new Student() { Age = 25 };
14        Console.WriteLine(isTeenAger(stud));
15        Console.ReadLine();
16    }
17}

Spiegazione:

The Lambda expression evolves from anonymous method by first removing the delegate keyword and parameter type and adding a lambda operator =>.

lambda-expression-1.png

Lambda Expression from Anonymous Method

The above lambda expression is absolutely valid, but we don't need the curly braces, return and semicolon if we have only one statement that returns a value. So we can eliminate it.

Also, we can remove parenthesis (), if we have only one parameter.

lambda-expression-2.png

Lambda Expression from Anonymous Method

Thus, we got the lambda expression: s => s.Age > 12 && s.Age < 20 where s is a parameter, => is the lambda operator and s.Age > 12 && s.Age < 20 is the body expression:

lambda-expression-structure.png

Lambda Expression Structure in C#

You can wrap the parameters in parenthesis if you need to pass more than one parameter, as below:

 1public class Student
 2{
 3    public int Id { get; set; }
 4    public string? Name { get; set; }
 5    public int Age { get; set; }
 6}
 7internal class Program
 8{
 9    delegate bool IsTeenAger(Student stud);
10    static void Main(string[] args)
11    {
12        IsTeenAger isTeenAger = delegate (Student s)
13        {
14            return s.Age > 12 && s.Age < 20;
15        };
16        Student stud = new Student() { Age = 25 };
17        Console.WriteLine(isTeenAger(stud));
18            Console.ReadLine();
19    }
20}

You can also give type of each parameters if parameters are confusing:

1(Student s,int youngAge) => s.Age >= youngAge;

It is not necessary to have at least one parameter in a lambda expression. The lambda expression can be specify without any parameter also.

1() => Console.WriteLine("Parameter less lambda expression")

Ad esempio:

 1internal class Program
 2{
 3    delegate void Print();
 4    static void Main(string[] args)
 5    {
 6        Print print = () => Console.WriteLine("This is parameter less lambda expression");
 7        print();
 8        Console.ReadLine();
 9    }
10}

You can wrap expressions in curly braces if you want to have more than one statement in the body:

1(s, youngAge) =>
2{
3    Console.WriteLine("Lambda expression with multiple statements in the body");
4    return s.Age >= youngAge;
5}

Altro esempio: lambda expression con due parametri

 1public class Student
 2{
 3    public int Id { get; set; }
 4    public string? Name { get; set; }
 5    public int Age { get; set; }
 6}
 7internal class Program
 8{
 9    delegate bool IsYoungerThan(Student stud, int youngAge);
10    static void Main(string[] args)
11    {
12        IsYoungerThan isYoungerThan = (s, i) => s.Age < i;
13        Student stud = new Student() { Age = 25 };
14        Console.WriteLine(isYoungerThan(stud, 30));
15    }
16}

You can declare a variable in the expression body to use it anywhere in the expression body, as below:

 1internal class Program
 2{
 3    delegate bool IsAdult(Student stud);
 4    static void Main(string[] args)
 5    {
 6        IsAdult isAdult = (s) => 
 7        {
 8            int adultAge = 18;
 9            Console.WriteLine("Lambda expression with multiple statements in the body");
10            return s.Age >= adultAge;
11        };
12        Student stud = new Student() { Age = 25 };
13        Console.WriteLine(isAdult(stud));
14        Console.ReadLine();
15    }
16}
17
18public class Student
19{
20    public int Id { get; set; }
21    public string? Name { get; set; }
22    public int Age { get; set; }
23}

Function Delegate and its use with lambda

.NET has a couple of built-in delegates types that you can use when declaring delegates.For the MathDelegate examples, you have used the following delegate: public delegate double MathDelegate(double value1, double value2); You can replace this delegate with one of the built-in types namely:

1Func<int, int, int>

like this,

 1
 2internal class Program
 3{
 4    static void Main(string[] args)
 5    {
 6        //modalità alternativa per definire un delegato
 7        Func<int, int, int> mathDelegate = (x, y) =>
 8        {
 9            Console.WriteLine("Add");
10            return x + y;
11        };
12        var result = mathDelegate(5, 2);
13        Console.WriteLine(result);
14        // output: 7
15        mathDelegate = (x, y) => x - y; ;
16        result = mathDelegate(5, 2);
17        Console.WriteLine(result);
18        // output: 3
19        Console.ReadLine();
20    }
21}

The Func<...> types can be found in the System namespace and they represent delegates that return a type and take 0 to 16 parameters. All those types inherit from MulticastDelegate so you can add multiple methods to the invocation list.

The lambda expression can be assigned to Func<in T, out TResult> type delegate. The last parameter type in a Func delegate is the return type and rest are input parameters. Visit Func delegate section of C# tutorials to know more about it.

Consider the following lambda expression to find out whether a student is a teenager or not.

1Func<Student, bool> isStudentTeenAger = s => s.Age > 12 && s.Age <20;
2Student std = new Student() { age = 21 };
3bool isTeen = isStudentTeenAger(std);// returns false

In the above example, the Func delegate expects the first input parameter to be of Student type and the return type to be boolean. The lambda expression s => s.age > 12 && s.age < 20 satisfies the Func<Student, bool> delegate requirement, as shown below:

func with lambda expression image

Func delegate with Lambda Expression

The Func<> delegate shown above, would turn out to be a function as shown below.

1bool isStudentTeenAger(Student s)
2{
3    return s.Age > 12 && s.Age < 20;
4}

Action Delegate and its use with lambda

Unlike the Func delegate, an Action delegate can only have input parameters. Use the Action delegate type when you don’t need to return any value from lambda expression.

If you want a delegate type that doesn’t return a value, you can use the System.Action types. They can also take 0 to 16 parameters, but they don’t return a value.

Here is an example of using the Action type

 1internal class Program
 2{
 3    static void Main(string[] args)
 4    {
 5        Action<int, int> mathDelegate = (x, y) =>
 6        {
 7            Console.WriteLine(x + y);
 8        };
 9        mathDelegate(5, 2);
10        // output: 7
11        mathDelegate = (x, y) => Console.WriteLine(x - y);
12        mathDelegate(5, 2);
13        // output: 3
14        Console.ReadLine();
15    }
16}

Esempio con le lambda expressions:

 1internal class Program
 2{
 3    static void Main(string[] args)
 4    {
 5        Action<Student> PrintStudentDetail = s => Console.WriteLine("Name:{0}, Age: {1}", s.Name, s.Age");
 6        Student std = new Student() { Name = "Bill", Age = 21 };
 7        PrintStudentDetail(std);
 8        Console.ReadLine();
 9    }
10}
11
12public class Student
13{
14    public int Id { get; set; }
15    public string? Name { get; set; }
16    public int Age { get; set; }
17}

Things start to become more complex when your lambda function starts referring to variables declared outside of the lambda expression or to this reference. Normally when control leaves the scope of the variable, the variable is no longer valid. But what if a delegate refers to a local variable. To fix this, the compiler generates code that makes the life of the captured variable at least as long as the longest-living delegate. This is called a closure.

Closure

A closure is a function that is bound to the environment in which it is declared. Thus, the function can reference elements from the environment within it’s body. In the case of a C# 2.0 anonymous method, the environment to which it is bound is its parenting method body. This means that local variables from the parenting method body can be referenced within the anonymous method's body. So, this code prints 0 to the console as expected:

 1internal class Program
 2{
 3    delegate void MyAction();
 4    static void Main(string[] args)
 5    {
 6    int x = 0;
 7    MyAction a = delegate { Console.WriteLine(x); };
 8    a();
 9    Console.ReadLine();
10    }
11}

Most developers don't have any problem with the code above. A local variable "x" is declared and initialized to 0. Then, a new delegate "a" of type Action is declared and assigned to an anonymous method that writes "x" to the console. Finally, "a" is called and the value of "x" (0) is printed to the console. The rub occurs when the code is changed like this:

 1internal class Program
 2{
 3    delegate void MyAction();
 4    static void Main(string[] args)
 5    {
 6        int x = 0;
 7        MyAction a = delegate { Console.WriteLine(x); };
 8        x = 1;
 9        a();
10        Console.ReadLine();
11    }
12}

Now, x is reassigned to a value of 1 before a is called. What will be output to the console?

It turns out that the answer is 1, not 0. The reason for this is that the anonymous method is a closure and is bound to its parenting method body and the local variables in it. The important distinction is that it is bound to variables, not to values. In other words, the value of x is not copied in when a is declared. Instead, a reference to x is used so that a will always use the most recent value ofx. In fact, this reference to x will be persisted even ifx goes out of scope. Consider this code:

 1internal class Program
 2{
 3    delegate void MyAction();
 4    static MyAction GetAction()
 5    {
 6        int x = 0;
 7        MyAction a = delegate { Console.WriteLine(x); };
 8        x = 1;
 9        return a;
10    }
11
12    static void Main(string[] args)
13    {
14        MyAction a = GetAction();
15        a();
16        Console.ReadLine();
17    }
18}

That will still print 1 to the console even though x is out of scope by the time that a is called. So, how is this achieved? Well, the good news is that this is handled through compiler magic. There isn’t any runtime support for closures. That means that you could use the same techniques to create a closure without using an anonymous method.

Points to Remember

  1. Lambda Expression is a shorter way of representing anonymous method.

  2. Lambda Expression syntax: parameters => body expression

  3. Lambda Expression can have zero parameter.

  4. Lambda Expression can have multiple parameters in parenthesis ().

  5. Lambda Expression can have multiple statements in body expression in curly brackets {}.

  6. Lambda Expression can be assigned to Func, Action or Predicate delegate.

  7. Lambda Expression can be invoked in a similar way to delegate.