Representing the user code by delegates ( 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/01/22/generic-implementation-of-iterateandapply-query-design-continued.aspx
Lets’ now consider the implementation of Apply function. In theory we could somehow interpret the string inside the Apply function, but having code in a form of string still causes problems. Consider the following example:
Dim number_names As New List(Of Integer)
number_names = IterateAndApply(numbers, _
"squares.Add(current_number.ToString())")
Take a look at the expression. While the code seems fine, our Apply function expects that the resulting type of the expression matches the type of the collection elements. This particular expression takes Integer and returns String.
We can add another type argument to Apply function to represent the return type of the expression:
Function Apply(Of T, S)(ByVal item As T, ByVal code As String) As S
However this just replaces one problem with another. Someone somewhere must specify S. Generic type argument T can be inferred from the function arguments (in particular from “item”). This is not the case for S. Since “code” is just a string, there is no way of knowing the type of the results of this expression. In fact there is no way to know at compile time that the expressions returns anything or even runs.
Consider this example
Dim number_names As New List(Of Integer)
number_names = IterateAndApply(numbers, "BLAH BLAH BLAH")
This is not going to run, but compiler has no idea.
Clearly a string is a very bad representation of user code. Besides the problems with interpreting it provides no information on what this code returns or whether this code will work.
Fortunately there is a way to write a piece of code and describe its arguments and return type. For that we need to put the code in a separate function and pass a reference to that function into the IterateAndApply as a delegate:
Dim numbers As New List(Of Integer)
numbers.Add(42)
Dim number_names As New List(Of Integer)
Dim MyCodeRef As Func(Of Integer, String) = AddressOf MyCode
number_names = IterateAndApply(numbers, MyCodeRef)
Function MyCode(ByVal current_number As Integer) As String
Return current_number.ToString
End Function
' =====================================
' implementation of IterateAndApply
'======================================
'delegate type to represent user expressions
' T is the type of argument
' S is the type of the result
Delegate Function Func(Of T, S)(ByVal arg As T) As S
' source - the collection that is being iterated
' code - reference to user supplied code to apply to each element
' returns a collection of results when user's code is applied to
' all lements.
Function IterateAndApply(Of T, S)(ByVal source As IEnumerable(Of T), _
ByVal code As Func(Of T, S) _
) As IEnumerable(Of S)
Dim result As New List(Of S)
'iterate the source
For Each item In source
'apply the code that user has provided to each element
result.Add(Apply(item, code))
Next
Return result
End Function
'function that applies given code to the given object and returns
'the result
Function Apply(Of T, S)(ByVal item As T, ByVal code As Func(Of T, S)) As S
Return code.Invoke(item)
End Function
The code above solves a lot of problems. First of all there is no need for “magic” to apply the user code. In fact Apply function becomes so trivial, there is no need to even have it. The inner loop of IterateAd Apply could simply look like this:
For Each item In source
'apply the code that user has provided to each element
result.Add(code.Invoke(item))
Next
Secondly, because user code is contained in a regular function and expressed as regular code, existing checks in the compiler can verify the syntax, type safety and everything else that can be verified at compile time.
Comments
- Anonymous
February 05, 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/01/30/representing-the-user-code-by-delegates-query-design-continued.aspx