Ugrás a tartalomhoz

.NET Programming Technologies

Gergely Kovásznai, Csaba Biró

Eszterházy Károly College

15. fejezet - LINQ (written by Gergely Kovásznai)

15. fejezet - LINQ (written by Gergely Kovásznai)

In software development, it is quite a frequent task to handle and display collections of data. Consequently, one often needs to apply operations to those collections, e.g., sorting the elements of a collection, filtering them, or linking two (or more) collections within a query (join).

In the previous sections, we have learned how to develop form-based applications, by using WPF. Let us image a form that displays a ListBox that contains data of employees! It would be cool to sort employees in the alphabetical order of their names, simply by clicking, or to display only those who work at the development division, or to filter out those on holiday.

One might find these kinds of tasks familiar from database management. In this section, we are introducing a .NET technology called LINQ (Language Integrated Query), which is capable to handle collections in an even more general way. In the followings, we are presenting the LINQ basics, and then the LINQ to Objects technology, which is about creating queries on collections of objects. In Section XVI we will learn about the LINQ to XML technology, and in Section XVII about LINQ to Entities.

But what is this all about? Let us suppose that we use a list of city names in our application:

List<string> cities = new List<string> {

        "Bologna", "Kiev", "Budapest",

        "Düsseldorf", "Madrid", "Sarajevo"

};

We would like to sort the city names by length. This can be done very easily, as follows:

       IEnumerable<string> citiesToDisplay = cities.OrderBy(c => c.Length);

In order to increase pleasures, before sorting we would like to filter the city names that are at least 8 character long:

IEnumerable<string> citiesToDisplay = cities

                .Where(c => c.Length >= 8)

                .OrderBy(c => c.Length);

In the latter code, one can notice several interesting (and maybe novel) aspects. Probably the special methods (Where, OrderBy) are the easiest to notice; they are called extension methods. The parameters used by extension methods are given in the form of ...=>...; an expression of this kind is called a lambda expression. Lambda expressions will be detailed in Section XV.1, and extension methods in Section Hiba! A hivatkozási forrás nem található..

As a really quick example, we would like to present another form for the above query:

IEnumerable<string> citiesToDisplay = from c in cities

                                                                                where c.Length >= 8

                                                                                orderby c.Length

                                                                                select c;

Such a declarative (SQL-like) expression, being absolutely equivalent with the previous syntax, is completely legal above C# 3.0, and is called a query expression, as will be detailed in Section Hiba! A hivatkozási forrás nem található..

Lambda Expressions

Lambda expressions are very intuitive elements in C#, above 3.0. But first, before starting to use them, let us try to clarify what kind of language elements they are!

First, it is worth to recall our knowledge about delegates. As it is well known, they behave like method types, and can be used to write methods that receive “method pointers” as parameters. Consider the following example: we would like to write a FilterDates method, which filters elements in a list of dates. We do not fix the filtering rules within the method, but we will rather pass those “rules” as parameters to the method. This is why the special parameter filter is specified, in which our filtering method (or rather a pointer to that method) will be passed. The delegate is used to declare a signature (i.e., parameters and return type) for the filtering method in advance. In the following example, we declare an own delegate, which requires a DateTime parameter and bool return type.

delegate bool DateFilter(DateTime date);

List<DateTime> FilterDates(List<DateTime> dates, DateFilter filter)

{

        List<DateTime> filteredDates = new List<DateTime>();

        foreach (DateTime d in dates)

                if (filter(d))

                        filteredDates.Add(d);

        return filteredDates;

}

It is very important that whatever method one would like to pass to FilterDates, its signature must be the same as the one of the delegate. For instance:

bool Filter21stCentury(DateTime date)

{

        return date.Year > 2000;

}

...

List<DateTime> dates = ...;

List<DateTime> datesToDisplay = FilterDates(dates, Filter21stCentury);

Suppose that we intend not to reference the Filter21stCentury method at any other location in the code. In the case of such “disposable” methods, it is quite inconvenient to define them in a nice and accurate way by assigning names to them. Above C# 2.0, it is also legal to pass anonymous methods as parameters. For example, the latter method call could be written as follows (without defining the filtering method separately):

List<DateTime> datesToDisplay = FilterDates(dates,

        delegate(DateTime d) { return d.Year > 2000; }

);

Lambda expressions, introduced in C# 3.0, can be considered as another, more intuitive syntax for anonymous methods. The above code can be written by using a lambda expression, as follows:

List<DateTime> datesToDisplay = FilterDates(dates, d => d.Year > 2000);

An important ingredient of a lambda expression is the => (arrow) operator. It is interesting that the compiler is able to infer the type of the parameter (d) from the context. One is also allowed to use more than one parameter, or even to write a lambda expression without any parameter. Moreover, the right-hand side of a lambda expression can even be a complete block (since it is actually the body of an anonymous method).

(a, b) => (a + b) / 2

() => 42

(x, y) => {

        if (x > y) return x;

        else return y;

}

In case of only one parameter, parentheses can be omitted. So can curly braces as well if the block on the right-hand side consists of one single „return …;”, and even the return command can be omitted.