|
|
 |
|
|
|
|
Visual Basic 2008 9.0 .NET Examples and Ebook
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Define a Query Method
| When we want to filter elements from all our IEnumerable(Of T) collections based on some predicate, we could add an ( extension ) method with a predicate argument to select the elements.
Next example defines a extension method GetWhere in module MyEnumerable : |
| Option Strict On
Option Infer On
Public Class Counter
Private m_Value As Integer
Public Property Value() As Integer
Get
Value = m_Value
End Get
Set(ByVal value As Integer)
m_Value = value
End Set
End Property
End Class
Public Module MyEnumerable
<System.Runtime.CompilerServices.Extension()> _
Public Function GetWhere(Of T)(ByVal source As IEnumerable(Of T), _
ByVal predicate As Func(Of T, Boolean)) _
As IEnumerable(Of T)
Dim getWhereList As New List(Of T)
For Each item As T In source
If predicate.Invoke(item) Then
getWhereList.Add(item)
End If
Next
GetWhere = getWhereList
End Function
End Module
Public Class Example1
Public Shared Sub Main()
Dim source = New Counter() {New Counter With {.Value = 10}, _
New Counter With {.Value = 20}, _
New Counter With {.Value = 30}}
Dim predicate As Func(Of Counter, Boolean) = _
Function(counter) counter.Value > 15
Dim query1 = MyEnumerable.GetWhere(source, predicate)
For Each counter As Counter In query1
Console.WriteLine(counter.Value)
Next
Console.WriteLine()
Dim query2 = source.GetWhere(Function(counter) counter.Value < 25)
For Each counter As Counter In query2
Console.WriteLine(counter.Value)
Next
Console.ReadLine()
End Sub
End Class Download Broncode |
| This extension method can now be called directly (1) or on any instance of type IEnumerable(Of T) (2).
For more details about these extensions, read the topic about extension methods. |
Up
Where
| Actually it was not necessary to define this extension. Class System.Linq.Enumerable already defines an extension method Where(Of TSource)(ByVal source As IEnumerable(Of TSource), ByVal predicate As Func(Of TSource, Boolean)) As IEnumerable(Of TSource) which is in scope when namespace System.Linq is imported. |
| Public Class Example2
Public Shared Sub Main()
Dim source = New Counter() {New Counter With {.Value = 10}, _
New Counter With {.Value = 20}, _
New Counter With {.Value = 30}}
Dim predicate As Func(Of Counter, Boolean) = _
Function(counter) counter.Value > 15
Dim query1 = System.Linq.Enumerable.Where(source, predicate)
For Each counter As Counter In query1
Console.WriteLine(counter.Value)
Next
Console.WriteLine()
Dim query2 = source.Where(Function(counter) counter.Value < 25)
For Each counter As Counter In query2
Console.WriteLine(counter.Value)
Next
Console.ReadLine()
End Sub
End Class Download Broncode |
| It is possible to reject the behaviour of the extension Where of Enumerable. This can be done by not importing the Linq namespace.
One could also do this by defining an own extension method ( like the first example did ) with the same name ( Where ). This closer extension would then have higher priority than the imported extension, at least when it is define "closer" then Linq. For instance in the same assembly as the clients using this user-defined extension method. If we change the name of extension method GetWhere to Where, the above example would execute our implementation, instead of the imported one, when a call to Where is made.
A method like Where of class Enumerable is often called a "query method", because it can be used to filter/query data. |
Up
OfType
| Another extremely usefull query extension method from class Enumerable is OfType(Of TResult)(ByVal source As IEnumerable) As IEnumerable(Of TResult).
This extension of IEnumerable allows us to filter all IEnumerable collections into a strongly typed IEnumerable(Of T) collection. |
| Public Class Example3
Public Shared Sub Main()
Dim source1 = New Object() {1, "two", "3"c, "four", 5.0}
Dim words As IEnumerable(Of String) = source1.OfType(Of String)()
For Each word As String In words
Console.WriteLine(word)
Next
Console.WriteLine()
Dim source2 As New ArrayList
source2.Add(New Counter With {.Value = 10})
source2.Add(New Counter With {.Value = 20})
source2.Add(New Counter With {.Value = 30})
Dim counters As IEnumerable(Of Counter) = source2.OfType(Of Counter)()
For Each counter As Counter In counters
Console.WriteLine(counter.Value)
Next
Console.ReadLine()
End Sub
End Class Download Broncode |
| Output : two
four
10
20
30 |
| All String elements for the IEnumerable collection source1 ( with elements of type Object ) are placed in a strongly type IEnumerable(Of String) collection.
Because methods like Where and OfType work on an instance IEnumerable(Of T), but also deliver an IEnumerable(Of T) object, they can be combined to more complex "query expressions" : |
| Public Class Example4
Public Shared Sub Main()
Dim source = New Object() {1, "two", "3"c, "four", 5.0}
Dim predicate1 As Func(Of String, Boolean) = _
Function(text) text.Contains("o")
Dim predicate2 As Func(Of String, Boolean) = _
Function(text) text.StartsWith("f")
Dim words As IEnumerable(Of String)
words = source.OfType(Of String).Where(predicate1).Where(predicate2)
For Each word As String In words
Console.WriteLine(word)
Next
Console.ReadLine()
End Sub
End Class Download Broncode |
Up
Deferred Query Evaluation
| Next example illustrates how queries are only evaluated the moment they are used, this is called "deferred query evaluation".
If, between two iterations, a query is manipulated, the manipulation will reflect on that second iteration (2). |
| Public Class Example5
Public Shared Sub Main()
Dim source = New Counter() {New Counter With {.Value = 10}, _
New Counter With {.Value = 20}, _
New Counter With {.Value = 30}}
Dim query = source.Where(Function(counter) True)
For Each counter As Counter In query
Console.WriteLine(counter.Value)
Next
Console.WriteLine()
source(1) = New Counter With {.Value = 200}
For Each counter As Counter In query
Console.WriteLine(counter.Value)
Next
Console.ReadLine()
End Sub
End Class Download Broncode |
| Output : 10
20
30
10
200
30 |
| Our own GetWhere from MyEnumerable did not have this behaviour : |
| Public Class Example6
Public Shared Sub Main()
Dim source = New Counter() {New Counter With {.Value = 10}, _
New Counter With {.Value = 20}, _
New Counter With {.Value = 30}}
Dim query = source.GetWhere(Function(counter) True)
For Each counter As Counter In query
Console.WriteLine(counter.Value)
Next
Console.WriteLine()
source(1) = New Counter With {.Value = 200}
For Each counter As Counter In query
Console.WriteLine(counter.Value)
Next
Console.ReadLine()
End Sub
End Class Download Broncode |
| The query was not deferred and immediately evaluated when the GetWhere method was called.
If a deferred query behaviour is desired, we can achieve this in our query method by delivering an iterator/enumerator, instead of immediately returning our filtered data. This iterator will only filter the internally referenced datasource the moment the iterator is used ( for instance in a For Each ) : |
Up
ToList and ToArray
| Class System.Linq.Enumerable defines some extensions of IEnumerable(Of T) that can be used to immediately execute a query, this in contrast to the default deferred behaviour. These extension are ToArray() As T() and ToList() As List(Of T). |
| Public Class Example8
Public Shared Sub Main()
Dim source = New Counter() {New Counter With {.Value = 10}, _
New Counter With {.Value = 20}, _
New Counter With {.Value = 30}}
Dim query = source.Where(Function(counter) True)
Dim queryArray As Counter() = query.ToArray()
Dim queryList As List(Of Counter) = query.ToList()
source(1) = New Counter With {.Value = 200}
For Each counter As Counter In queryArray
Console.WriteLine(counter.Value)
Next
Console.WriteLine()
For Each counter As Counter In queryList
Console.WriteLine(counter.Value)
Next
Console.ReadLine()
End Sub
End Class Download Broncode |
| Output : 10
20
30
10
20
30 |
| The queries assigned to queryArray and queryList are immediately evaluated. |
Up
Singleton Queries
| Query extension of IEnumerable(Of T) that deliver a single value are also immediately evaluates.
A few of those singleton query methods are illustrated below : |
| Public Class Example9
Public Shared Sub Main()
Dim source = New Counter() {New Counter With {.Value = 10}, _
New Counter With {.Value = 20}, _
New Counter With {.Value = 30}}
Console.WriteLine(source.Any())
Console.WriteLine(source.All(Function(counter) counter.Value Mod 2 = 0))
Console.WriteLine(source.Average(Function(counter) counter.Value))
Console.WriteLine(source.ElementAt(2).Value)
Console.WriteLine(source.First().Value)
Console.ReadLine()
End Sub
End Class Download Broncode |
| Output : True
True
20
30
10 |
| Any returns True is elements are present.
All returns True if the predicate is correct for all elements.
Average returns the average of all Decimals of all element. Average expects a selector that specifies how the element can be converted to a Decimal. |
Up
Projection with Select
| Extension method 'Select(Of TSource, TResult)(ByVal source As IEnumerable(Of TSource), ByVal selector As Func(Of TSource, TResult)) As IEnumerable(Of TResult)' can be used to project the result of a query ( of 'TSource elements ) into a instance of type 'TResult'.
selector defines the expression used to project the resultvalues. This is often in an anonymous type. |
| Objects of anonymouse type are used to gather the projected information. |
This version ( published on 2008-06-24 ) is printed from http://www.studyvb.com, visit the website for more recent information.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|