LINQ can be thought of as a ?sub-language? within the VB and C# .NET programming languages. In both cases, these managed languages provide a large number of LINQ query operators, which allow us to build strongly typed queries that can be applied to a wide variety of data stores (databases, XML documents or object collections).
While it is true that most developers will tend to use the provided operators, they are simply shorthand notations for calling numerous extension methods provided by the System.Linq.Enumerable class (and possibly other classes based on what you are doing with your query?but Enumerable is the key).
In this blog post, I?d like to illustrate how a typical LINQ query ? created with the LINQ query operators ? can be transformed into the underlying object model. Hopefully this will give you a much better idea of what is really going on behind the scenes!
As it turns out, a great many of the methods of Enumerable have been prototyped to take delegates as arguments. In particular, many methods require a generic delegate of type Func, defined within the System namespace of System.Core.dll. For example, consider the following members of Enumerable that extend the IEnumerable(Of T) interface. Notice how each version of the generic Where(Of T) method takes a Func delegate as an argument:
1: ' Overloaded versions of the Enumerable.Where(Of T)() method.
2: ' Note the second parameter is of type System.Func.
3: <Extension()> _
4: Public Shared Function Where(Of TSource)(ByVal source As IEnumerable(Of TSource), _
5: ByVal predicate As Func(Of TSource, Boolean)) As IEnumerable(Of TSource)
6:
7: <Extension()> _
8: Public Shared Function Where(Of TSource)(ByVal source As IEnumerable(Of TSource), _
9: ByVal predicate As Func(Of TSource, Integer, Boolean)) As IEnumerable(Of TSource)
The Func delegate (as the name implies) represents a pattern for a given function with a set of arguments and a return value. If you were to examine this type using the Visual Studio object browser, you?d notice that the Func delegate can take between zero and four input arguments (here typed T1, T2, T3, and T4 and named arg1, arg2, arg3, and arg4), and a return type denoted by TResult:
1: ' The various formats of the generic System.Func delegate.
2: Public Delegate Function Func(Of T1, T2, T3, T4, TResult)(ByVal arg1 As T1, _
3: ByVal arg2 As T2, ByVal arg3 As T3, ByVal arg4 As T4) As TResult
4:
5: Public Delegate Function Func(Of T1, T2, T3, TResult)(ByVal arg1 As T1, _
6: ByVal arg2 As T2, ByVal arg3 As T3) As TResult
7:
8: Public Delegate Function Func(Of T1, T2, TResult)(ByVal arg1 As T1, _
9: ByVal arg2 As T2) As TResult
10:
11: Public Delegate Function Func(Of T, TResult)(ByVal arg As T) As TResult
12:
13: Public Delegate Function Func(Of TResult)() As TResult
Given that many members of System.Linq.Enumerable demand a delegate as input, when invoking them, we can either manually create a new delegate type and author the necessary target methods, make use of a proper lambda expression, or simply use LINQ query operators, which hide the low-level details from view. Regardless of which approach you take, the end result is identical.
While it is true that making use of VB LINQ query operators is far and away the simplest way to build a LINQ query expression, let?s walk through each of these possible approaches just so you can see the connection between the VB LINQ query operators, the Func delegate the underlying Enumerable type.
Now, consider the following method, which build a simple LINQ query that finds a subset of data from a String array:
1: Sub QueryStringArrayWithOperators()
2: Console.WriteLine("***** Using LINQ Query Operators *****")
3:
4: Dim currentVideoGames As String() = {"Morrowind", "BioShock", _
5: "Half Life 2: Episode 1", "The Darkness", _
6: "Daxter", "System Shock 2"}
7:
8: ' Build a LINQ query with VB LINQ operators.
9: Dim subset = From g In currentVideoGames _
10: Where g.Length > 6 Order By g Select g
11:
12: For Each s As String In subset
13: Console.WriteLine("Item: {0}", s)
14: Next
15: Console.WriteLine()
16: End Sub
The obvious benefit of using VB LINQ query operators to build query expressions is the fact that the Func delegates and calls on the Enumerable type are out of sight and out of mind, as it is the job of the VB compiler to perform this translation. To be sure, building LINQ expressions using various query operators (From, In, Where, Order By, etc.) is the most common and most straightforward approach.
Keep in mind that the LINQ query operators used here are simply shorthand versions for calling various extension methods defined by the Enumerable type. Consider the following QueryStringsWithEnumerableAndLambdas() method, which is processing the local string array now making direct use of the Enumerable extension methods and various lambda expressions:
1: Sub QueryStringsWithEnumerableAndLambdas()
2: Console.WriteLine("***** Using Enumerable / Lambda Expressions *****")
3:
4: Dim currentVideoGames() As String = {"Morrowind", "BioShock", _
5: "Half Life 2: Episode 1", "The Darkness", _
6: "Daxter", "System Shock 2"}
7:
8: ' Build a query expression using extension methods
9: ' granted to the Array via the Enumerable type.
10: Dim subset = currentVideoGames.Where(Function(game) game.Length > 6). _
11: OrderBy(Function(game) game).Select(Function(game) game)
12:
13: ' Print out the results.
14: For Each game In subset
15: Console.WriteLine("Item: {0}", game)
16: Next
17: Console.WriteLine()
18: End Sub
Here, we are calling the generic Where(Of T)() method off the string array object, granted to the Array type as an extension method defined by Enumerable. The Enumerable.Where(Of T)() method makes use of the System.Func(Of T, TResult) delegate type. The first type parameter of this delegate represents the IEnumerable(Of T)-compatible data to process (an array of strings in this case), while the second type parameter represents the method that will process said data.
Given that we have opted for a lambda expression (rather than directly creating an instance of Func or crafting an anonymous method), we are specifying that the ?game? parameter is processed by the statement game.Length > 6, which results in a Boolean return type.
The return value of the Where(Of T)() method has implicitly typed, but under the covers we are operating on an OrderedEnumerable type. From this resulting object, we call the generic OrderBy(Of T, K)() method, which also requires a Func(Of T, TResult) delegate parameter. Finally, from the result of the specified lambda expression, we select each element, using once again a Func(Of T, TResult) under the covers.
It is also worth remembering that extension methods are unique in that they can be called as instance-level members upon the type they are extending (System.Array in this case) or as shared members using the type they were defined within. Given this, we could also author our query expression as follows:
1: Dim subset = Enumerable.Where(currentVideoGames, _
2: Function(game) game.Length > 6). _
3: OrderBy(Function(game) game).Select(Function(game) game)
As you may agree, building a LINQ query expression using the methods of the Enumerable type directly is much more verbose than making use of the VB LINQ query operators. As well, given that the methods of Enumerable require delegates as parameters, you will typically need to author lambda expressions to allow the input data to be processed by the underlying delegate target.
Finally, if we want to build a query expression using the really verbose approach, we could avoid the use of lambda syntax and directly create delegate targets for each Func type. Here is the final iteration of our query expression, modeled within a new class type named VeryComplexQueryExpression:
1: Class VeryComplexQueryExpression
2: Public Shared Sub QueryStringsWithRawDelegates()
3: Console.WriteLine("***** Using Raw Delegates *****")
4:
5: Dim currentVideoGames As String() = {"Morrowind", "BioShock", _
6: "Half Life 2: Episode 1", "The Darkness", _
7: "Daxter", "System Shock 2"}
8:
9: ' Build the necessary Func delegates.
10: Dim searchFilter As New Func(Of String, Boolean)(AddressOf Filter)
11: Dim itemToProcess As New Func(Of String, String)(AddressOf ProcessItem)
12:
13: ' Pass the delegates into the methods of Enumerable.
14: Dim subset = currentVideoGames.Where(searchFilter). _
15: OrderBy(itemToProcess).Select(itemToProcess)
16:
17: ' Print out the results.
18: For Each game In subset
19: Console.WriteLine("Item: {0}", game)
20: Next
21: Console.WriteLine()
22: End Sub
23:
24: ' Delegate targets.
25: Public Shared Function Filter(ByVal str As String) As Boolean
26: Return str.Length > 6
27: End Function
28: Public Shared Function ProcessItem(ByVal str As String) As String
29: Return str
30: End Function
31:
32: End Class
If you were to now run the application to test each possible approach, it should not be too surprising that the output is identical regardless of the path taken:

Keep the following points in mind regarding how LINQ query expressions are represented under the covers:
- Query expressions are created using various VB LINQ query operators.
- Query operators are simply shorthand notations for invoking extension methods defined by the System.Linq.Enumerable type.
- Many methods of Enumerable require delegates (Func in particular) as parameters.
- Methods requiring a delegate parameter can instead be passed a lambda expression.
- Lambda expressions are shorthand notations for allocating a raw delegate and manually building a delegate target method.
Happy coding!