Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

C# Lectures - Lecture 9:Lambda expressions

0.00/5 (No votes)
18 Sep 2016 1  
9th from my series. This is relatively short but it is introduction to big LINQ part

Full Lectures Set


Introduction

In this article I'm going to talk about anonymous functions that are named lambda expressions. Lambda expressions is the relatively new technology that allows you create delegates or expression tree types. Using lambda expressions you can create functions that you pass as arguments to another functions or return them as value of function calls. Also lambda expressions are more than useful with LINQ. Most developers and writers describe lambda expressions together with delegates and events, but as this is relatively new technology, I've decided to have separate article about it. Since version 3, C# has supported lambda expressions. Lambda expressions have been used in computer languages as far back as LISP; they were conceptualized in 1936 by Alonzo Church, an American mathematician. These expressions provide shorthand syntax for specifying an algorithm.

Anonymous types

Before diving into lambda expressions I want to highlight quickly another short topic - anonymous types. Anonymous types provide a convenient way to encapsulate read-only properties to single object without definition of type for this object. While compilation compiler generates a name for the anonymous type, but it is not visible for developer. Type for the read-only properties in anonymous type is also defined by compiler. By itself anonymous types doesn't seems to be something cool, but in combination with LINQ and Lambda expressions they give a real power. We'll review it later on in this chapter.

Following code demonstrates usage of anonymous type:

            //---------------ANONYMOUS TYPES------------------------
            Console.WriteLine("-----------ANONYMOUS TYPES----------------");
            var anonym1 = new { int_val = 1, double_val = 0.11, string_val = "some string" };
            Console.WriteLine("anonym1 type is: " + anonym1.GetType());
            Console.WriteLine("double_val type is: " + anonym1.double_val.GetType());
            Console.WriteLine("int_val type is: " + anonym1.int_val.GetType());
            Console.WriteLine("string_val type is: " + anonym1.string_val.GetType());
Result of it will be:

Anonymous types limitations:

  • They can have only public read-only properties
  • They don't have methods or other type members
  • You can cast anonymous type only to object
  • If two anonymous types have equal definition they are treated by compiler as same type and share same metadata

There are several ways of initialization for anonymous types. Few of them are provided below:

int i = 10;
 string s = "string value";
 var anonym2 = new { int_val = i, string_val = s };
 var anonym3 = new { i, s };
 var anonym4 = new { anonym2, anonym3 };

Building lambda expression, lambda operator

When you create lambda expression you should use lambda operator =>. Lambda operator separates input variable from the left side with the lambda body on the right. Lambda operator can be read as "goes to". In lambda expression you put input parameters, if you have them, on the left side of operator and  expression or statement from the right side of the lambda operator. So lambda expression looks like:

<arguments if any>   =>   <expression>

or

<arguments if any>   =>   <statement block>

Initially before lambda expressions there were designed anonymous methods that you can build path them to functions that expect delegates. Sometimes this is very convenient and useful\readable in code. Lambda then was created to provide more specific syntax for anonymous methods as delegates and for sure they are more than useful for LINQ query expressions as they provide with ability to write compact methods that you path as arguments for subsequent evaluation.

Below is an example how we replace delegate with lambda function:

//Declarign the delegate:

    public delegate int MyDelegate(string s, int i);

//Implement the caller and delegate:

        public static void DelegateCaller(MyDelegate input)
        {
            int res = input("Delegate Caller", 5);
            Console.WriteLine("Delegate result is: " + res);
        }
        public static int DelegateFunction(string s, int i)
        {
            Console.WriteLine("Delegate function string: " + s + " int:" + i);
            return 1;
        }

//calling via delegate and via lambda

            Console.WriteLine("-----------LAMBDA EXPRESSIONS----------------");
            //calling by delegate
            DelegateCaller(new MyDelegate(DelegateFunction));
            //creating lambda delegate
            MyDelegate del = (str, dig) =>
            {
                Console.WriteLine("Delegate lambda string: " + str + " int: " + dig);
                return 2;
            };
            DelegateCaller(new MyDelegate(del));

To play with the example you can download sources attached to this article

Lambda expression syntaxes

  1. You can specify parameters in parenthesis is you need to path more than one parameter:
                 (car, engineSize) => car.engine >= engine Size
  1. You can specify types for parameters to avoid confusion
<code>                 (ClassCar car, int engineSize) => car.engine >= engine Size</code>
  1. You can have lambda expression without parameters:
<code>                 () => Console.WriteLine("Parameter less lambda");</code>
  1. If you have multiple statements in body they should be in curly braces:
x =>

{

   Console.WriteLine("Few statements lambda");

   return x*x;

}
  1. You can use local variable in expression body that will be visible only inside your lambda expression
x =></code>

 

Variable scope in lambdas

In lambdas you can refer to variables that are in the scope of method where they are defined and in type where they are defined. If your will use such variable in scope of lambda it will be stored in memory for lambda even if in other cases it would be garbage collected. Please see example below that demonstrates how local variable in method where lambda is defined is still in memory with proper value even so in usual cases it will not work:

//declaring delegate:

    public delegate int SomeDelegate(int input);

//implementing sample function

        public void ScopeExample()
        {
            //local variable that will be hold for delegate
            //even after ScopeExample is running out of the
            //scope and released from memory
            int j = 5;
            m_del = (x) =>
            {
                Console.WriteLine("Input parameter is : " + x);
                Console.WriteLine("Local variable is: " + j);
                return j + x;
            };
            int i = m_del(5);
            Console.WriteLine("Delegate internal result is: " + i);
        }

//testing

            //delegates variable scope
            Program test = new Program();
            test.ScopeExample();
            int k = test.m_del(10);
            Console.WriteLine("Delegate external result is: " + k);

 

You can download sources attached to play with it

I'm copying below from MSDN rules for variable scope in lambdas:

  • A variable that is captured will not be garbage-collected until the delegate that references it goes out of scope.
  • Variables introduced within a lambda expression are not visible in the outer method.
  • A lambda expression cannot directly capture a ref or out parameter from an enclosing method.
  • A return statement in a lambda expression does not cause the enclosing method to return.
  • A lambda expression cannot contain a goto statement, break statement, or continue statement whose target is outside the body or in the body of a contained anonymous function.

Sources

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here