Excerpt from Visual Basic 2008 Programmer's Reference
By Rod Stephens
Many of the newest features in Visual Studio 2008 were added to support LINQ (Language Integrated Query). Extension methods, lambda functions, anonymous types, and many of the other features new to Visual Studio were added to make building LINQ easier.
Visual Basic doesn't really execute LINQ queries. Instead it converts them into a series of function calls that perform the query. While the LINQ query syntax is generally easier to use, it is sometimes helpful to understand what those function calls look like.
The following sections explain the general form of these function calls. They explain how the function calls are built, how you can use these functions directly in your code, and how you can extend LINQ to add your own LINQ query methods.
Method-Based Queries
Suppose a program defines a List(Of Customer) named all_customers and then defines the following query expression. This query finds customers that have AccountBalance values less than zero, orders them by AccountBalance, and returns an IEnumerable object that can enumerate their names and balances. (Example program LinqLambda, which is available for download from the Visual Basic 2008 Programmer's Reference Wrox site, defines a simple Customer class and performs a similar query.)
Dim q1 = _
From cust In all_customers _
Where cust.AccountBalance < 0 _
Order By cust.AccountBalance _
Select cust.Name, cust.AccountBalance
To perform this selection, Visual Basic converts the query into a series of function calls to form a method-based query that performs the same tasks as the original query. For example, the following method-based query returns roughly the same results as the original LINQ query.
Dim q2 = all_customers. _
Where(AddressOf OwesMoney). _
OrderBy(AddressOf OrderByAmount). _
Select(AddressOf SelectFields)
This code calls the all_customers list's Where method. It passes that method the address of the function OwesMoney, which returns True if a Customer object has a negative account balance.
The code then calls the OrderBy method of the result returned by Where. It passes the OrderBy method the address of the function OrderByAmount, which returns a Decimal value that OrderBy can use to order the results of Where.
Finally, the code calls the Select method of the result returned by OrderBy. It passes Select the address of a function that returns a CustInfo object representing each of the selected Customer objects. The CustInfo class contains the Customer's Name and AccountBalance values.
The exact series of method calls generated by Visual Studio to evaluate the LINQ query is somewhat different from the one shown here. The version shown here uses OwesMoney, OrderByAmount, and SelectFields methods that I defined in the program to help pick, order, and select data. The method-based query generated by Visual Basic uses automatically generated anonymous types and lambda expressions so it is much uglier.
The following code shows the OwesMoney, OrderByAmount, and SelectFields methods.
Private Function OwesMoney(ByVal c As Customer) As Boolean
Return c.AccountBalance < 0
End Function
Private Function OrderByAmount(ByVal c As Customer) As Decimal
Return c.AccountBalance
End Function
Private Function SelectFields(ByVal c As Customer, _
ByVal index As Integer) As CustInfo
Return New CustInfo() With _
{.CustName = c.Name, .Balance = c.AccountBalance}
End Function
Function OwesMoney simply returns True if a Customer's balance is less than zero. The Where method calls OwesMoney to see if it should pick a particular Customer for output.
Function OrderByAmount returns a Customer's balance. The OrderBy method calls OrderByAmount to order Customer objects.
Function SelectFields returns a CustInfo object representing a Customer.
That explains where the functions passed as parameters come from, but what are Where, OrderBy, and Select? After all, Where is called as if it were a method provided by the all_customers object. But all_customers is a List(Of Customer) and that has no such method.
In fact, Where is an extension method added to the IEnumerable interface by the LINQ library. The generic List class implements IEnumerable so it gains the Where extension method.
Similarly LINQ adds other extension methods to the IEnumerable interface such as Any, All, Average, Count, Distinct, First, GroupBy, OfType, Repeat, Sum, Union, and many more.