Delayed Execution ( 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/06/07/query-pattern-composability-query-design-continued.aspx
Here is another important example of a queryable type. There are two differences from the previous examples:
It uses System.Func delegate type that is defined in System.Core.dll . There is nothing special in using this type. VB compiler will accept any delegate type that matches the queryable pattern. However using predefined delegate type reduce the amount of code to type.
Another very important difference is that the Select does not create another collection with projected elements. Instead it creates an object that “knows” how to produce projected elements when asked for them. There are many advantages in doing that. First of all we may not need all the elements so it makes sense to delay computation until the elements are needed. Secondly for a big source collection, the resulting collection will take much less space. This is especially important if you compose a query with multiple projections applied in sequence.
Basically instead of creating another collection for every Select in a composed query we will build a pipeline of enumerable types that represent projection delegates. When another element is requested from the query, by delegating the request through the chain of these enumerable types, we will get the next element from the source collection and every projection in the pipeline will be applied to the element.
This concept is commonly known as a delayed execution. It does complicate the debugging somewhat as there seem to be too many levels of indirection. However the benefits far outweigh that.
Here is the sample
(it is not as big as it seems as most parts are just required to implement IEnumerable pattern):
Class EnumerableQueryable(Of T)
Implements IEnumerable(Of T)
Private elements As IEnumerable(Of T)
Sub New(ByVal elements As IEnumerable(Of T))
Me.elements = elements
End Sub
Public Function [Select](Of S)(ByVal selector As Func(Of T, S)) As EnumerableQueryable(Of S)
Dim new_enum = New Enumerable4Select(Of T, S)(Me, selector)
Dim new_col As New EnumerableQueryable(Of S)(new_enum)
Return new_col
End Function
Public Function GetEnumerator() As System.Collections.Generic.IEnumerator(Of T) Implements System.Collections.Generic.IEnumerable(Of T).GetEnumerator
Return elements.GetEnumerator
End Function
Public Function GetEnumerator1() As System.Collections.IEnumerator Implements System.Collections.IEnumerable.GetEnumerator
Return GetEnumerator()
End Function
End Class
Class Enumerable4Select(Of T, S)
Implements IEnumerable(Of S)
Private collection As IEnumerable(Of T)
Private selector As Func(Of T, S)
Public Sub New(ByVal coll As IEnumerable(Of T), ByVal func As Func(Of T, S))
collection = coll
selector = func
End Sub
Public Function GetEnumerator() As System.Collections.Generic.IEnumerator(Of S) Implements System.Collections.Generic.IEnumerable(Of S).GetEnumerator
Return New Enumerator4Select(collection.GetEnumerator(), selector)
End Function
Public Function GetEnumerator1() As System.Collections.IEnumerator Implements System.Collections.IEnumerable.GetEnumerator
Return GetEnumerator()
End Function
Class Enumerator4Select
Implements IEnumerator(Of S)
Private collection As IEnumerator(Of T)
Private selector As Func(Of T, S)
Public Sub New(ByVal coll As IEnumerator(Of T), ByVal func As Func(Of T, S))
collection = coll
selector = func
End Sub
Public ReadOnly Property Current() As S Implements System.Collections.Generic.IEnumerator(Of S).Current
Get
Return selector(collection.Current)
End Get
End Property
Public ReadOnly Property Current1() As Object Implements System.Collections.IEnumerator.Current
Get
Return Current
End Get
End Property
Public Function MoveNext() As Boolean Implements System.Collections.IEnumerator.MoveNext
Return collection.MoveNext()
End Function
Public Sub Reset() Implements System.Collections.IEnumerator.Reset
collection.Reset()
End Sub
Private disposedValue As Boolean = False ' To detect redundant calls
' IDisposable
Protected Overridable Sub Dispose(ByVal disposing As Boolean)
If Not Me.disposedValue Then
If disposing Then
' TODO: free managed resources when explicitly called
End If
' TODO: free shared unmanaged resources
End If
Me.disposedValue = True
End Sub
#Region " IDisposable Support "
' This code added by Visual Basic to correctly implement the disposable pattern.
Public Sub Dispose() Implements IDisposable.Dispose
' Do not change this code. Put cleanup code in Dispose(ByVal disposing As Boolean) above.
Dispose(True)
GC.SuppressFinalize(Me)
End Sub
#End Region
End Class
End Class
Here is some code to test the above sample:
Module Module1
Sub Main()
Dim col As New EnumerableQueryable(Of Integer)(New Integer() {1, 2, 3})
Dim q = From i In col Select i_sqr = i * i Select i_sqr + 1
For Each i In q
Console.WriteLine(i)
Next
End Sub
End Module
Comments
- Anonymous
July 05, 2008
PingBack from http://kenya.adultpornostories.com/vbnetgetenumerator.html