【黃忠成】LINQ首部曲: LINQ To Object Part 2 - Using VB.NET(3)

iDotNetSpace發表於2008-05-19
Union
 
   Union函式可以將兩個IEnumerable(Of T)物件整合成一個, 如程式17。
[程式17]
Sub TestUnion()
        Dim p1() As Integer = {1, 3, 5, 7, 9, 11}
        Dim p2() As Integer = {2, 4, 6, 8, 10, 11}
        Dim result = p1.Union(p2)
        For Each item In result
            Console.WriteLine(item)
        Next
        Console.ReadLine()
    End Sub
執行結果圖10。
[圖10]
1
3
5
7
9
11
2
4
6
8
10
如結果所示,兩個IEnumerable(Of T)物件被整合成一個了,在呼叫Union函式時,我們也可以指定一個實作了IEqualComparaer介面的物件來改變結果,如程式18所示。
[程式18]
Sub TestUnion2()
        Dim p1() As String = {"code6421", "tom", "cathy"}
        Dim p2() As String = {"code6421", "Tom", "cathy"}
        Dim result = p1.Union(p2, _
            StringComparer.Create(System.Globalization.CultureInfo.CurrentCulture, True))
        For Each item In result
            Console.WriteLine(item)
        Next
        Console.ReadLine()
    End Sub
此例的意思是,以無關大小寫的字串比對方式,比對兩個IEnumerable(Of T)物件中的元素,避免列出重複的元素,圖11是此例的執行結果。
[圖11]
code6421
tom
cathy
 
Intersect
 
 Intersect函式可以找出兩個IEnumerable(Of T)物件中相同的元素,如程式19。
[程式19]
Sub TestIntersect()
        Dim p1() As Integer = {1, 3, 5, 7, 9, 11}
        Dim p2() As Integer = {2, 4, 6, 7, 10, 11}
        Dim result = p1.Intersect(p2)
        For Each item In result
            Console.WriteLine(item)
        Next
        Console.ReadLine()
    End Sub
此例的執行結果僅會列出相同的數字,也就是7與11。在呼叫Intersect函式時,也可以像Union函式般,指定一個 實作了IEqualComparer介面的物件做為比對基準,如程式20。
[程式20]
Sub TestIntersect2()
        Dim p1() As String = {"code6421", "tom", "cathy"}
        Dim p2() As String = {"code6421", "Tom", "cathy"}
        Dim result = p1.Intersect(p2, _
          StringComparer.Create(System.Globalization.CultureInfo.CurrentCulture, True))
        For Each item In result
            Console.WriteLine(item)
        Next
        Console.ReadLine()
    End Sub
此例是以無關大小寫字串的方式比對,執行結果圖12。
[圖12]
code6421
tom
cathy
Tom、tom因字串是以不分大小寫比對的方式,所以僅列出一個。
 
Except
 
 Except函式允許傳入一個IEnumerable(Of T)物件,以其來比對原始的IEnumerable(Of T)物件中的元素,僅列出未出現在所傳入IEnumerable(Of T)物件中的元素,見程式21。
[程式21]
Sub TestExcept()
        Dim p1() As Integer = {1, 3, 5, 7, 9, 11}
        Dim p2() As Integer = {2, 4, 6, 8, 10, 11}
        Dim result = p1.Except(p2)
        For Each item In result
            Console.WriteLine(item)
        Next
        Console.ReadLine()
End Sub
執行結果如圖13。
[圖13]
1
3
5
7
9
如結果所示,11已經被排除了。與Union函式相同,Except函式也允許傳入一個實作了IEqualComparer介面的物件,用來當做比對的基準,如程式22。
[程式22]
Sub TestExcept2()
        Dim p1() As String = {"code6421", "tom", "cathy2"}
        Dim p2() As String = {"code6421", "Tom", "cathy"}
        Dim result = p1.Except(p2, _
         StringComparer.Create(System.Globalization.CultureInfo.CurrentCulture, True))
        For Each item In result
            Console.WriteLine(item)
        Next
        Console.ReadLine()
    End Sub
此例僅會列出cathy2這個元素。
 
OfType、Cast
 
 OfType、Cast是LINQ To Object Framework所提供的轉型函式,可以將一個IEnumerable(Of T)物件中的元素轉型為所要求的型別,兩者不同之處在於OfType函式於轉型失敗時並不會丟出例外,而Cast則會丟出例外,見程式23。
[程式23]
Sub TestOfType()
        Dim ar As New ArrayList()
        ar.Add("Test1")
        ar.Add("Test2")
        ar.Add("Test3")
        ar.Add("Test4")
        ar.Add(16)
        Dim result = ar.OfType(Of String)()
        'Of Type will not raise invalid type-casting exception.
        'if you need receive the exception at type-casting,use Cast.
        For Each item In result
            Console.WriteLine("Type is {0} and value is {1}", item.GetType(), item)
        Next
        Console.ReadLine()
    End Sub
此例執行結果如圖14。
[圖14]
Type is System.String and value is Test1
Type is System.String and value is Test2
Type is System.String and value is Test3
Type is System.String and value is Test4
 
Sum、Average、Min、Max、Count、
 
 LINQ To Object Framework也提供了Sum、Min、Max、Average、Count等之類常見於SQL指令中的功能函式,Sum用於加總、Min用於取出最小值、Max則用於取出最大值、Count則用於計算符合條件的元素數目,基本上這四個函式的用法大致相同,因此此處我只以Sum函式做為代表例,見程式24。
[程式24]
Sub TestSum()
        Dim list() As Integer = {18, 20, 25}
        Dim result = list.Sum()
        Console.WriteLine(result)
        Console.ReadLine()
End Sub
無意外,此例的執行結果為63,無引數的Sum函式僅作用於數值型別,如Int32、Int64、Decimal、Double、Single等等,當需加總的是一個物件時,就必需傳入一個Lambda Expression來協助Sum函式運算,見程式25。
[程式25]
Sub TestSum2()
        Dim persons() = { _
                         New With {.Name = "code6421", .Age = 18, .Address = "Taipai"}, _
                         New With {.Name = "jeffray", .Age = 18, .Address = "USA"}, _
                         New With {.Name = "catch", .Age = 18, .Address = "USA"}, _
                         New With {.Name = "joe", .Age = 18, .Address = "NY"} _
                         }
        Dim result = persons.Sum(Function(p) p.Age)
        Console.WriteLine(result)
        Console.ReadLine()
End Sub
 
Aggregate
 
 Sum函式用於計算加總值,但若需要的不僅僅是加法呢?例如乘法、減法、除法等等,此時另一個Aggregate函式就能派上用場了,見程式26。
[程式26]
Sub TestAggreate()
       Dim list() As Integer = {18, 20, 25}
        Dim result = list.Aggregate(Function(x, y) x * y)
        Console.WriteLine(result)
        Console.ReadLine()
End Sub
此例的結果是9000,也就是18*20*25後的結果,另外於呼叫Aggregate函式時,也可以指定一個起始值,如程式27。
[程式27]
Sub TestAggreate2()
        Dim list() As Integer = {18, 20, 25}
        Dim result = list.Aggregate(9000, Function(x, y) x * y)
        Console.WriteLine(result)
        Console.ReadLine()
End Sub
此例的結果是,9000*18*20*25 = 81000000。
 
語法之外,明確使用LINQ TO Object Framework的理由
 
 在本文中的範例,偶而會使用Where函式而不是使用LINQ Expression所提供的where
,讀者或許對此舉會有所疑惑,既然已經有了對應的LINQ Expression,又何必使用函式呼叫呢?簡單的說,LINQ Expression的轉換支援並無法完全取代函式呼叫,以Where例子來說,當使用函式呼叫時,我們可以寫下很複雜的處理函式,如程式28。
[程式28]
Sub TestWhereFunction()
        Dim list() As String = {"code6421", "tom", "cathy"}
        Dim result = list.Where(AddressOf CompareData)
        For Each item In result
            Console.WriteLine(item)
        Next
        Console.ReadLine()
End Sub
 
Function CompareData(ByVal val As String) As Boolean
        Dim conn As New SqlClient.SqlConnection()
        'do something of you want..............
        Return True
End Function
這是無法以單純的LINQ Expression來辦到的。
註:C#的Lambda Expression可以允許寫得很複雜,如下:
var p5 = p1.Where(x =>
                              {
                                  bool result = false;
                                  SqlConnection conn = new SqlConnection("....");
                                  conn.Open();
                                  return result;
                              });
不過VB.NET 2008並不支援此類寫法,所以還是得走Function模式。
 
 
Dim、Dim xxx() As Type的差異
 
 一般來說,你可以以Dim來宣告一個陣列,而不事先指定其型別,如下所示:
Dim list() = {1, 2, 3}
此時當你透過list來呼叫某些LINQ函式時,會發生要求傳入一個Lambda Expression的情況,例如Sum就得寫成下面這樣:
Dim list() = {1, 2, 3}
Dim result = list.Sum(Function(p) p)
這是因為list陣列被當成Object陣列的緣故,直接於Dim時給予型別,那麼就可以用下面的方式呼叫Sum函式。
Sub Main()
        Dim list() As Integer = {1, 2, 3}
        Dim result = list.Sum()
        Console.WriteLine(result)
        Console.ReadLine()
End Sub
所以,如非必要,使用Dim As Type的方式,可以避免因編譯器的猜測所產生的不必要問題。
 
 
還是效能的課題
 
 在LINQ To Object Framework中,效能並非是首要考量的課題,因此許多功能都隱涵效能的問題,例如where,預設是遍覽IEnumerable(Of T)物件中的元素,並一一以傳入的Lambda Expression來驗證,這在某些情況下是不適用的,因為 有時我們只是想尋找某一個符合條件的元素,而且該IEnumerable(Of T)物件是擁有既定排序的,此時採取Binary Search的演演算法來搜尋所要的資料是較有效率的。因此!設計師應該將LINQ To Object Framework視為是一個查詢演演算法的Framework,但其不保證以最快的方式來完成所要求的動作。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/12639172/viewspace-277789/,如需轉載,請註明出處,否則將追究法律責任。

相關文章