Closures and Linq in C# 3.0
Keith posted a great article demonstrating Linqs capabilities. There he was asked if his examples where "closures". Let me take this opportunity to explain how closures are used in Linq by clarifying what a closure is and showing how the Linq queries are translated at compile time(from linq queries -> extension method calls -> static methods calls).
Closures
What are closures really? A closure or formally lexical closure is a function that can refer to and alter the values of variables defined in its declaring scope. In C# closures are made possible by using anonymous delegates. Below is a very contrived example demonstrating an occurence of a closure in C#.
int x = 10;
Action<int> addToX = new Action<int>( delegate(int y )
{
x = x + y;
}
);
addToX(10);
Console.WriteLine(x);
Here the variable x can be accessed and modified inside the anonymous delegate.
Closures in C# are made possible through a compile time translation called Lifting or closure conversion. Try doing an ILDASM on the above sample code to see lifting in action.
Linq Query Translation
For the purpose of illustration, well use Keith's example on how to find a person with age greater than 25.Since it is the only one demonstrating the use of closure in a linq query.
//specify age
int selectedAge = 25;
//get a list of people with age greater than 25
var ageIsGreaterThan25 =
from l
in listOfPerson
where l.BirthDate >= DateTime.Now.AddYears(-selectedAge)
select l;
Linq queries are basically syntactic sugar over extension methods. Meaning during compile time Linq queries are converted into calls to its extension method. The where clause can now be expressed as:
listOfPerson.Where( p => p.BirthDate >= DateTime.Now.AddYears(-selectedAge) )
Then, these calls are converted into actual calls to the corresponding static methods in the class declaring the extension method. Depending on which one is imported via a using directive. So in case of Linq To Objects, it would be Sequence. The call to the Where method now looks like this.
Sequence.Where<Person>( listOfPerson, p => p.BirthDate >= DateTime.Now.AddYears(-selectedAge) )
Putting it All Into Context
The second parameter to Queryable.Where is a lambda expression of type Func<T, bool>. Func<T, bool> is a generic delegate which has a signature of public delegate RT Func<T0, RT>(T0 a0). (I already explained about the Func delegates in a previous article so I wont be talking about them in much detail here)
Hence we can further translate the code to:
Sequence.Where<Person>( listOfPerson, delegate (Person p) { return p.BirthDate >= DateTime.Now.AddYears(-selectedAge); } )
Which brings us to the anonymous delegate call and therefore a closure.