Query pattern, Composability ( Query Design continued ):
****
This post is a part of a series of posts about query design. For the previous post see: https://blogs.msdn.com/vladimirsadov/archive/2007/02/19/polymorphic-selector-and-extension-methods-query-design-continued.aspx
Here I want to summarize what exactly the minimum requirements are for a type to be considered queryable with some explanations why we have such rules.
Quaryable type requirements:
1. There must be a method called Select that can be called with the collection instance. Note that this can be either an instance method or an extension method. As long as you can make a call like col_instance.Select(..) it is acceptable as a select operator.
Why: by having a proper Select method a type indicates that it can be used in query expressions. It is also the method that represents “Select” in the query syntax.
Dim result = From el in col_inst Select el.ToString
translates into:
Dim result = col_inst.Select(...)
2. Select must take exactly one argument. The argument must be of a function delegate type that takes one argument.
Why: The argument must be a delegate because compiler will generate a function that takes one argument and will pass this function to the Select method in form of a delegate.
3. While the selector delegate can be (and often is) a generic type, the delegate’s argument type (let’s call it T) must be completely inferable from the collection type.
Why:T must be known because it indicates the type of collection elements. Since the collection is completely in charge of iterating itself we need some way to know what type its elements have. In the above example T would be the type of el. Knowing the type of iteration variable compiler can verify that projection expression (el.ToString in the above example) makes sense. T is also used to provide intellisense when you type “el.” .
Example of very simple queryable type:
Public Delegate Function SelectorDelegate(ByVal arg As Integer) As Integer
Class MyQueryable
Public elements() As Integer
Public Function [Select](ByVal projection As SelectorDelegate) As MyQueryable
Dim new_col As New MyQueryable
new_col.elements = elements.Clone
For I As Integer = 0 To elements.Length - 1
new_col.elements(I) = projection(elements(I))
Next
Return new_col
End Function
End Class
You can test this queryable type with code like this:
Module Module1
Sub Main()
Dim arr As Integer() = New Integer() {1, 2, 3}
Dim col As New MyQueryable()
col.elements = arr
Dim q = From I In col Select square = I * I
For Each t In q.elements
Console.WriteLine(t)
Next
End Sub
End Module
Output:
1,4,9
Note that result of Select in our example is MyCollection that is a queryable type. This is not a requirement of queryable pattern. Select could return any type it wants. However returning a queryable type is very convenient as you can “compose” query by chaining Select operators:
This is a valid expression in VB. The second Select will be applied to the result of the first select
Dim q1 = From I In col Select square = I * I Select sq_2 = square * 2
If you replace the query in the previous example with the new query, you will get output:
2,8,18
Comments
Anonymous
June 08, 2007
This post is a part of a series of posts about query design. For the previous post see: http://blogs.msdn.com/vladimirsadov/archive/2007/06/07/query-pattern-composability-query-design-continued.aspxAnonymous
June 10, 2007
This post is a part of a series of posts about query design. For the previous post see: http://blogs.msdn.com/vladimirsadov/archive/2007/06/07/query-pattern-composability-query-design-continued.aspxAnonymous
June 11, 2007
This post is a part of a series of posts about query design. For the previous post see: http://blogs.msdn.com/vladimirsadov/archive/2007/06/07/query-pattern-composability-query-design-continued.aspx