Friday, 13 January 2012

Lambda Expression in C#


Lambda expression is an inline delegate introduced with C # 3.0 languages. It's a concise way to represent an anonymous method. It provides syntax to create and invoke functions. Although Lambda expressions are simpler to use than anonymous methods, they do slightly differ on how they are implemented. Both anonymous methods and Lambda expressions allow you define the method implementation inline; however, an anonymous method explicitly requires you to define the parameter types and the return type for a method. Lambda expression uses the type inference feature of C# 3.0 which allows the compiler to infer the type of the variable based on the context.

Lambda expression can be broken down into parameters followed by execution code. e.g.:

Parameter => executioncode.

The left hand side represents zero or more parameters followed by the lambda symbol => which is used to separate the declaration of a parameter from the implementation of the method. Lambda expression is then followed by the statement body.

Lambda expression allows us to pass functions as arguments to a method call. I will start with a simple example of lambda expression which returns even numbers from a list of integers.

List<Customers> CustColls = new List<Customers>();
IEnumerable<String> EnuCusID = CustColls.Select(i => i.CustomerID);
OR
IEnumerable<Customers> EnuCustomers = CustColls.Select(i => i);

Looking at the lambda expression assigned to the EnuCusID variable, you will notice few things that are different from anonymous methods. First, we are not using delegate keyword anywhere in our code.
In this EnuCusID variable only the CustomerID are store as a string array IEnumerable<String>. In EnuCustomers variable IEnumerable<Customers> collection are store.

Another place where parentheses are required in lambda expressions is when you want to use a parameter in multiple blocks of code inside the lambda expression such as follows:

delegate void WriteMultipleStatements(int i);
public static void MultipleStatementsInLamdas()
{
    WriteMultipleStatements write = i =>
    {
        Console.WriteLine("Number " + i.ToString());
        Console.WriteLine("Number " + i.ToString());
    };
    write(1);
}

C# 3.0 defines the number of generic delegates that you can assign to your lambda expression instead of var keyword which infers the type. Let's take a look at an example of using a few of those generic delegates:

public static void GenericDelegates()
{
    List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7 };
    Func<int, bool> where = n => n < 6;
    Func<int, int> select = n => n;
    Func<int, string> orderby = n => n % 2 == 0 ? "even" : "odd";
    var nums = numbers.Where(where).OrderBy(orderby).Select(select);
    ObjectDumper.Write(nums);
}

In the above example, we are using three different extension methods: whereorderby and select. The where extension method takes a generic delegate with int parameter and a return type of boolean to identity if a certain element be included in the output sequence.

The select extension method takes an integer parameter and returns an integer, but it could return anything that you want the result to be transformed into — before being sent to the output sequence. In the orderby  extension method, we are taking the integer parameter and using it to identify if it is even or odd. Based on that, we sort the results. It would have been extremely cumbersome if we had to define three different delegates for each of these lambda expressions. With the introduction of generic delegates in C# 3.0, it is fairly non trivial to assign our lambda expressions to generic delegates and pass those delegates to the extension methods. Generic delegates come in pretty handy and help you avoid writing common delegates which was common in .NET 1.1 and .NET 2.0 (since there were no generic delegates that came out of the box). Generic delegates allow you to define up to 4 parameters and 1 return type so you can have a delegate that may look something like this:

Func<int, bool, string, double, decimal> test;
If your method or delegate does not meet the criteria then you have to manually declare a delegate that takes those parameters. Generic delegates usually cover majority of scenarios but in cases where it does not meet your needs, feel free to write a custom delegate.
There are cases where type inference may not return the data type that you really want the lambda expression to return. In those cases, we can explicitly specify the parameter type on the lambda expression. For example:
Func<double, int> expr = (x) => x / 2;

The above expression returns a compiler error because by dividing a double, the inferred type is actually going to be double. However, you are assigning the lambda expression to a delegate that has a return type of int. If int is what you really want to return from the method, then you are better off casting the expression body to int to indicate your intent as shown below:

Func<double, int> expr = (x) => (int)x / 2;

Lambda expressions are basically of two types. One is considered a simple expression where everything is inferred and consists of an expression only. The second type of lambda expression is statement blocks which are composed of braces and return type. Let's write a lambda expression in both forms to see the difference:

//example showing two types of lambda expressions
  public static void ExplicitParametersInLambdaExpression()
  {
      Func<int, int> square = x => x * x;
      Func<int, int> square1 = (x) => { return x * x; };

      Expression<Func<int, int>> squareexpr = x => x * x;

      Expression<Func<int, int>> square2 = (int x) => { return x * x; };
      //does not compile.
  }

Joining Using Lamda

If you have a set of business objects that are related to another set of business objects by a foreign key, you can use the Join method in a Lambda expression to join the two sets of business objects into one. You can then use the resulting set as a single list. This is useful when you need to perform operations on a single  list, like for binding.

class EMP
{
    public int EID { get; set; }
    public string ENAME { get; set; }
}
class EPROJECT
{
    public int EID { get; set; }
    public string PROJECT{ get; set; }
}

Joining with List<EMP> and List<EPROJECT> make an anonyms Type 'rows' shown below

List<EMP> list1 = new List<EMP>
{
    new EMP{ EID=1, ENAME="A"},
    new EMP{ EID=2, ENAME="B"}
};
List<EPROJECT> list2 = new List<EPROJECT>
{
    new EPROJECT{ EID=1, PROJECT="AA"},
    new EPROJECT{ EID=1, PROJECT="BB"}
};
var rows = list1.Join(list2, a => a.EID, b => b.EID, (a, b) => new
{
    EName = a.ENAME,
    Project = b.PROJECT
});

In this example, the desired result is a list of EMP but with the EID replaced with the EName of the EMP.
Notice in the above code that the Join method is used on the list1. This ensures that all EMP are in the list along with their matching EID. This is called the outer list.
The first parameter to the Join method is the inner list. This is the list joined to the first (outer) list. In this case, it is list2 to join the appropriate EPROJECT to their EMP.
The joining is done using keys. So the next two parameters define the two keys. In this example, the name of the keys is the same: EID.
So, the second parameter of the Join method defines the outer list's key. In this case it is the key in EMP class that is used for the join.
The third parameter of the Join method defines the inner list's key. In this example, it is the key in the EMP class that is used for the join.
The fourth parameter defines the values to return. In this example, the code is creating an anonymous type. The anonymous type has the EName along with the EPROJECT's Project.
Ex: 1

string constr = @"Server=x;Database=xx;User Id =sa;Password =;Integrated Security=false";
using (Northwind context = new Northwind(new SqlConnection(constr)))
{
    IQueryable<Customers> parentQuery = context.Customers;
    if (custZip.Text != string.Empty)
        parentQuery = parentQuery.Where(c => c.PostalCode == custZip.Text);
    if (custCountry.Text != string.Empty)
        parentQuery = parentQuery.Where(c => c.Country == custCountry.Text);

    IQueryable<Orders> childQuery = context.Orders;
    if (shipZip.Text != string.Empty)
        childQuery = childQuery.Where(o => o.ShipPostalCode == shipZip.Text);
    if (shipCountry.Text != string.Empty)
        childQuery = childQuery.Where(o => o.ShipCountry == shipCountry.Text);

    var query = from customer in parentQuery
                 join order in childQuery
                 on customer.CustomerID equals order.CustomerID
                 select order;

    Orders[] results = query.ToArray();
}

Ex: 2
Generating Joins Dynamically

string constr = @"Server=x;Database=xx;User Id =sa;Password =;Integrated Security=false";
using (Northwind context = new Northwind(new SqlConnection(constr)))
 {
     IQueryable<Customers> parentQuery = context.Customers;
     // generate where calls for parent query …

     IQueryable<Orders> childQuery = context.Orders;
     // generate where calls for child query …

     var query = from customer in parentQuery
                 join order in childQuery
                 on customer.CustomerID equals order.CustomerID
                 select order;

     Orders[] results = query.ToArray();
 }

Ex: 3
string constr = @"Server=x;Database=xx;User Id =sa;Password =;Integrated Security=false";
using (Northwind context = new Northwind(new SqlConnection(constr)))
{
    IQueryable<Customers> parentQuery = context.Customers;
    // generate where calls for parent query …

    IQueryable<Orders> childQuery = context.Orders;
    // generate where calls for child query …

    IQueryable<Orders> query = parentQuery
                                    .Join(childQuery,
                                          customer => customer.CustomerID,
                                          order => order.CustomerID,
                                    (customer, order) => order);
    Orders[] results = query.ToArray();
}

Ex: 4
List<T>.ConvertAll<>() with Lambda

List<T> has many useful methods for dealing with sequences and collections. One such method is the ConvertAll<>() method. All those who have used the List<T>.ConvertAll<>() are aware how useful this method is to convert the current List<T> elements to another type, and return a list of converted elements. However, an instance of a conversion delegate must be passed to this method, which knows how to convert each instance from the source type to the destination type.

List<int> li = new List<int>() { 1, 2, 2, 2, 2, 3, 4, 8, 55, 6 };
List<String> str = li.ConvertAll<String>(delegate(int x) { return x.ToString(); });

Ex: 5

List<Customers> CustColls = new List<Customers>();
IEnumerable<Customers> EnuCus = CustColls.Select(i => i);
var orderItems = CustColls.OrderBy(i => i.ContactName).ThenByDescending(i => i.Country);

DLINQ

DLINQ or LINQ to SQL provides a runtime framework to work with Relation Database objects. It translates LINQ queries into SQL queries automatically. We can create the total snapshot of the database using LINQ objects so that the entire relationship can be created in application classes and objects. There are tools that can create those objects automatically; we can make use of them in our program. We can make use of Northwind classes to get the flavor of DLINQ. .NET makes use of enhanced Attributes to define classes.

[Table(Name = "Employee")]
public class Employee
{
    [Column(Id = true)]
    public string EmpID;

    [Column]
    public string Name;
}

XLINQ

Similar to DLINQ, XLINQ means using LINQ to XML documents. .NET extended the little jem LINQ to create a number of classes which can manipulate XML documents very easily. Compared to XML DOM, XLINQ is much more flexible because it does not require you to always have a document object to be able to work with XML. Therefore, you can work directly with nodes and modify them as content of the document without having to start from a root XmlDocument object. This is a very powerful and flexible feature that you can use to compose larger trees and XML documents from tree fragments.

XDocument bookStoreXml =
        new XDocument(
         new XDeclaration("1.0", "utf-8", "yes"),
         new XComment("Bookstore XML Example"),
         new XElement("bookstore", new XElement("genre",
           new XAttribute("name", "Fiction"),
           new XElement("book", new XAttribute("ISBN",
             "10-861003-324"), new XAttribute("Title", "A Tale of Two Cities"),
            new XAttribute("Price", "19.99"),
            new XElement("chapter", "Abstract...", new XAttribute("num", "1"),
             new XAttribute("name", "Introduction")),
            new XElement("chapter", "Abstract...", new XAttribute("num", "2"),
             new XAttribute("name", "Body")
             )
            )
           )
          )
        );
In this example, we have created an XML document using XDocument class. The XDocument class may contain XElement which are the XML elements. XDeclaration is the class to create XML declaration. XAttribute creates an XML attributes. XComment creates XML comments. In this example Root element bookstore holds all the child elements inside it. We can pass an entire tree of XElement inside any XElement thus it gives easier way to create XML documents.

After reading, creating or manipulating the xml document, we can save the document easily to the disk using save method on XDocument object.

bookStoreXml.Save(@"C:\XMLDocuments\Bookstore.xml");

It will save the entire document to the disk.

To read the document again and create the entire tree structure of object in application end we call the load method of XElement.

XElement.Load(@"C:\XMLDocuments\Bookstore.xml");

We can also use parse method if we want to load the xml from a string.

You can query XML document in the same way as we use in objects. We can make use of some methods Ancestors (which returns all the ancestor elements in IEnumerable list), Descendants (returns all the descendants of the element), XElement (returns the child element with name), XElements (returns all the child elements of the current node), Nodes (returns the content of the current node)

Posted By: Mr. Palash Paul

"Save a Tree" – Please consider the environment before printing this article.

3 comments: