Saturday, September 13, 2008

Linq Query Operators

Much of Linq's query operators are method extensions which take a typed iterator for input, and return a modified iterator of the same time as their output.

List<Client> clients = new List<Client>();
IEnumerable<Client> it1 = clients as IEnumerable<Client>;
IEnumerable<Client> it2 = it1.Where(c => c.Rating > 5);


This pattern allows for the piping of multiple query operators, the first one's output providing the next one's input :

List<Client> clients = new List<Client>();
IEnumerable<Client> it1 = clients as IEnumerable<Client>;
IEnumerable<Client> it2 = it1.Where(c => c.Rating > 5); IEnumerable<Client> it3 = it2.Where(c => c.Rating < 10);
IEnumerable<Client> it4 = it3.Where(c => c.Rating != 7);


The fact that Linq's query operators are built from iterators has important implications, as it enables the deferred evaluation of a query. That is because C# iterators only return their elements when they are asked for. For example, the following iterator will only perform it's first time consuming operation :

public IEnumerable<int> GetItems()
{
yield return this.TimeConsumingOperation(1);
yield return this.TimeConsumingOperation(2);
yield return this.TimeConsumingOperation(3);
}

foreach (int item in iterator.GetItems())
{
int firstItem = item;
break;
}


This means that a query operation will only be evaluated when its items are iterated over, and for only as many items as the iteration asks for.

No comments: