Wednesday, October 17, 2007

LINQ - extension methods in action


As promised in one of previous posts - more about extension methods. Let's have a look at... LINQ!


You remember extension methods, whose simplest definition would be:
Extension methods are basically static methods that are callable through an instance syntax
[The Evolution Of LINQ And Its Impact On The Design Of C#]

It seems like they were introduced in C# 3.0 to make LINQ work fully.

LINQ is a language feature allowing to query collections in a SQL-like manner (SELECT statement), something like:
var locals = from c in customers
where c.ZipCode == 91822
select new { FullName = c.FirstName + “ “ +
c.LastName, HomeAddress = c.Address };


So - where are extension methods here?

Well - the sample above is actually syntactic sugar, being equivalent to:
var locals =
customers
.Where(c => c.ZipCode == 91822)
.Select(c => new { FullName = c.FirstName + “ “ + c.LastName,
HomeAddress = c.Address });

Now, let's assume customers are of type IEnumerable<Customer>. IEnumerable<T> has only one method: GetEnumerator<T>. This means that both Where and Select must be extension methods, which mix the SQL-like functionalities into all IEnumerable<T>-s!

In fact the Where method is very simple:
class EnumerableExtensions
{
public delegate T Func(A0 arg0);

public static IEnumerable Where(this IEnumerable source, Func predicate)
{
foreach (T element in source)
{
if (predicate(element)) yield return element;
}
}
}

It takes the IEnumerable<T> source parameter - which is preceded by the this keyword (so it's going to extend the IEnumerable<T> type), and the predicate delegate.
It can then use the predicate to tell whether each element in the source satisfies the "WHERE clause".
Those, that do - are put into the resulting collection of the Where method (using the yield keyword).

So, basically the following:
customers.Where(c => c.ZipCode == 91822)

will result in calling the static Where method against the customers object, with the Func<T, bool> predicate set to this anonymous method:

delegate {
if (c.ZipCode == 91822) yield return c;
}

Yep, that's what happened to the:
c => c.ZipCode == 91822
thing, which is actually a lambda expression - another syntactic sugar in C# 3.0, on which more some other time.

No comments: