From Anonymous Types to Lambda Expression (Sample)

I decided to post a short sample taken from the C# 3.0 Enhancements & LINQ HOL I hold last week.

C# 2.0 introduced the concept of anonymous methods, which allow code blocks to be written “in-line” where delegate values are expected.

Lambda Expressions provide a more concise, functional syntax for writing anonymous methods. They end up being super useful when writing LINQ query expressions – since they provide a very compact and type-safe way to write functions that can be passed as arguments for subsequent evaluation. But let’s start from the beginning …

You first need to create a new C# Console Application using Visual Studio 2008. Use NewLanguageFeatures as project name. Once you have created the project, define the Customer class and inside the generated Program class create a new method called CreateCustomers that creates a list of Customers.

public class Customer

{

    public int CustomerID { get; private set; }

    public string Name { get; set; }

    public string City { get; set; }

    public Customer()

    {

    }

    public Customer(int ID)

    {

         CustomerID = ID;

    }

    public override string ToString()

    {

         return Name + "\t" + City;

    }

}

public class Customer

{

    static List<Customer> CreateCustomers()

    {

    return new List<Customer>

    {

    new Customer(1) { Name ="Ken Casada", City="Zürich"},

    new Customer(2) { Name ="Maria Anders", City="Berlin"},

    new Customer(3) { Name ="Laurance Lebi", City="Paris"},

    new Customer(4) { Name ="Ann Devon", City="London"},

   new Customer(5) { Name ="Paolo Accorti", City="Torino"},

   new Customer(6) { Name ="Franz Wilson", City="Portland"},

   new Customer(7) { Name ="Simon Crowther", City="London"},

    new Customer(8) { Name ="Elisabeth Brown", City="London"}

    };

    }

}

Suppose now you wanted to find all the customers in a given city. To do this write a method (FindCustomerByCityOption1) that takes a list of customers and a city, and returns only a list of customers located in the specific city.

To do this, first update the Main method to use the 2 methods (CreateCustomers and FindCustomerByCityOption1) as shown in the following code:

static void Main(string[] args)

{

    var customers = CreateCustomers();

    foreach (var c in FindCustomersByCityOption1(customers, "London"))

         Console.WriteLine(c);

    Console.ReadLine();

}

Define the FindCustomersByCityOption1 (which will use a quite old syntax) inside the program class.

public static List<Customer> FindCustomersByCityOption1

                             ( List<Customer> customers, string city

                             )

{

   FilterCustDelegate myDelegate = new FilterCustDelegate(FilterCustMethod);

   return customers.FindCustomersByCity(myDelegate, city);

}

As you can note from the above code, the FindCustomerByCityOption1 method uses a delegate of type FilterCustDelegate, which is initialized with the FilterCustMethod method. The delegate (and the method of course) takes a Customer object and a string variable (representing the city) and returns a boolean value indicating if the current customer is located in the specified city.

Define now the FilterCustDelegate inside the NewLanguageFeatures namespace and the FilterCustMethod in the Program class as shown in the following code:

namespace NewLanguageFeatures

{

    public delegate bool FilterCustDelegate(Customer c, string s);

    …

}

class Program

{

    public static bool FilterCustMethod(Customer c, string city)

  {

       return c.City == city;

    }

You still need to define the FindCustomersByCity method (called inside the FindCustomersByCityOption1 method you created before) inside a new Extensions class (to be defined inside the NewLanguageFeatures namespace) as shown in the following code:

public static class Extensions

{

    public static List<Customer> FindCustomersByCity

                                 ( this List<Customer> customers, FilterCustDelegate myDelegate, string city

                                 )

  {

        List<Customer> result = new List<Customer>();

        foreach (Customer c in customers)

  {

            if (myDelegate(c, city))

  {

               result.Add(c);

            }

        }

        return result;

    }

}

Note that this method is an extension method of the List<customer> type: the first parameter of type List<customer> is marked with the this keyword. This method loops through the customers list and returns the list of the customers located in the specified city by calling the delegate of type FilterCustDelegate you instantiated before (FindCustomersByCityOption1).

You can now press Ctrl+F5 to run the application and verify that it displays just customers located in London, then press any key to close the console window and terminate the application.

You now can add now another method (FindCustomerByCityOption2) that makes use of C# 2.0 anonymous methods, which allow code blocks to be written “in-line” where delegate value are expected as shown in the following code. Note how the code block written in-line (return c.City == arg2; ) reflects the code previously defined by the FilterCustMethod method (which is not needed anymore)!

public static List<Customer> FindCustomersByCityOption2

                                   (

                                      List<Customer> customers,

                                      string city

                                   )

{

    FilterCustDelegate myDelegate = new FilterCustDelegate

    (

         delegate (Customer c, string arg2)

         {

             return c.City == arg2;

         }

    );

    return customers.FindCustomersByCity(myDelegate, city);

}

Update the Main method to call the new FindCustomersByCityOption2 method as shown below:

static void Main(string[] args)

{

    var customers = CreateCustomers();

    foreach (var c in FindCustomersByCityOption2(customers, "London"))

         Console.WriteLine(c);

    Console.ReadLine();

}

Press again Ctrl+F5 to run the application and verify that it displays again just customers located in London, then press any key to close the console window and terminate the application.

Now replace the anonymous method with an equivalent lambda expression. To do this let’s define another method (FindCustomersByCityOption3) and update again the Main method as shown in the following code:

public static List<Customer> FindCustomersByCityOption3

                             (

                                  List<Customer> customers,

                                  string city

                             )

{

  FilterCustDelegate myDelegate = (c, arg2) => c.City == arg2;

    return customers.FindCustomersByCity(myDelegate, city);

}

Note that a lambda expression is written as a parameter list (c, arg2) , followed by the => token, and then an expression or statement c.City == arg2. The parameter of a lambda expression can be explicitly or implicitly typed. In an implicitly typed parameter list (as in our sample), the types of the parameters are inferred from the context in which the lambda expression is used. In addition, if a lambda expression has a single, implicitly typed parameter, the parentheses may be omitted from the parameter list. If you wanted to be more explicit and at the same write everything in just one line of code, you could also have been written:

return customers.FindCustomersByCity
(

                     (Customer c, string arg2) => c.City == arg2,
city
);

static void Main(string[] args)

{

    var customers = CreateCustomers();

    foreach (var c in FindCustomersByCityOption3(customers, "London"))

         Console.WriteLine(c);

    Console.ReadLine();

}

Press Ctrl+F5 to run the application and verify that it displays again just customers located in London, then press any key to close the console window and terminate the application.

Hope this helps,

Ken