LINQ首部曲: LINQ To Object Part 2 - Using VB.NET

Proteus仿真实战:ADC0832时序解析与51单片机精准电压采集 本文详细解析了ADC0832模数转换芯片在Proteus仿真环境中的时序控制与51单片机驱动实现。通过实战代码示例和精度优化技巧,帮助开发者掌握电压采集的关键技术,包括时序配置、数据校验和噪声抑制等核心要点,提升嵌入式系统设计的精准度与可靠性。 阅读详情
 
LINQ首部曲: LINQ To Object Part 2 - Using VB.NET
 
文/黃忠成
 
 續前文,這一篇一樣是發表於Run! PC雜誌,同時收錄於【極意之道-.NET Framework 3.5資料庫開發聖典-ASP.NET篇】一書中,於此我針對VB.NET做了調整。
 
回到起點: LINQ 語法規則
 
 任何語言皆有其基本語法規則存在,LINQ雖然被打造成一個與編譯器無關的Framework,但為了讓設計師能更方便、更直覺的使用LINQ Expression,目前已知的C#、VB.NET都定義了一套專屬的LINQ Expression語法,雖然各個編譯器所提供的語法略有差異,但基本上差距不遠,本節以VB.NET所提供的LINQ Expression為基準,為讀者們介紹LINQ Expression的語法規則及各式各樣的應用,表1是筆者所整理出來的VB.NET LINQ Expression語法規則。
[表1]
u   query-expression
 From <alias> In <source|query-expression> <<where-expression>|<group-by expression>|<join expression>|<order-by-expression>> <select-expression>
u   where-expression
 left-variable <operand> right-variable
n   operand
(= | <> | < | > | >= | <=)
 left-variable <operand> query-expression(single value result)
 query-expression(single value result)   <operand> left-variable
query-expression(single value result)   <operand> query-expression(single value result)
 variable.<function> (Boolean result)
 variable.<function> operand right-variable
 variable.<function> operand variable<function>
 <shared function> operand right-varaible
 <shared function> operand <shared function>
 <function> operand right-varaible
 <function> operand <shared function>
 <VB.NET Expression single line> (Boolean result)
 <VB.NET Expression single line> operand right-variable
 <VB.NET Expression single line> operand <VB.NET Expression single line>
 where-expression < And | Or | AndAlso | OrElse>, where-expression <*>
u   group-by expression
 group <alias | query-expression> by <alias.member|query-expression(single value result)> into <variable>
group <alias | query-expression> by <alias.member| query-expression(single value result)>,<alias.member| query-expression(single value result)> into <variable>
u   join-expression
 join <alias > in <source|queryt-expression> on <where-expression(equal only)>
 join <alias > in <source|queryt-expression> on <where-expression(equal only)> into <variable>
u   order-by-expression
 order by <alias-member> <asc|desc>
 order by <alias-member>,<alias-member> <asc|desc>
u   select-expression
 Select <alias member>
 Select new With {anonymous type declaration}
乍看之下,此表似乎相當複雜,其實她還蠻簡單的,以此表搭配下文的語法分類解說,應能讓讀者們快速的掌握VB.NET中的LINQ Expression語法。
 
Query-Expression
 
 LINQ Expression中至少會包含一個query-expression,表一中對query-expression的定義如下。
From <alias> In <source|query-expression> <<where-expression>|<group-by expression>|<join expression>|<order-by-expression>> <select-expression>
<alias>指的是欲在此query-expression中使用的別名,更明確說是一個變數,編譯器會將In後面的運算式所產生的結果放到此變數中,此處有一點必須特別指出,In後面的<source|query-exprssion>代表著來源,<soruce>可以是一個IEnumerable(Of T)類型的物件,法則上<source>也可以由另一個<query-exprssion>所取代,我們將此稱為Nested query-expression(巢狀Query)。Query-expression中可以包含Where語句<where-expression>、Group By語句<group expression>、Join語句<join expression>、Order By修飾句<order-by-exprssion>及Select語句<select-expression>,因應不同的語句,其內或允許query-exprssion的出現,下例是一個簡單的query-expression。
Dim list() As String = {"1111", "2222", "3333"}
Dim p = From o In list Select o
 
Where-expression
 
 Query-expression中可以包含where-expression,用來對物件作查詢動作,下例是一個簡單的例子。
Dim list() As String = {"1111", "2222", "3333"}
Dim p = From o In list Where o = "2222" Select o
如表一所示,where-expression的left-expression及 right-expression也可以由query-expression所取代,前提是這個query-expression必須傳回單一值,如程式1。
[程式1]
Sub TestComplexWhere2()
        Dim p1() = { _
                     New With {.Name = "code6421", .Address = "Taipai"}, _
                     New With {.Name = "tom", .Address = "Taipai"}, _
                     New With {.Name = "jeffray", .Address = "NY"} _
                   }
        Dim p2() = { _
                     New With {.Name = "code6421", .Title = "Manager"}, _
                     New With {.Name = "tom", .Title = "Director"}, _
                     New With {.Name = "jeffray", .Title = "Programmer"} _
                   }
        Dim p3() = { _
                   New With {.Name = "code6421", .Hand = "Manager", .Address = "Taipai"}, _
                   New With {.Name = "tom", .Hand = "Director", .Address = "Taipai"}, _
                   New With {.Name = "jeffray", .Hand = "Programmer", .Address = "Taipai"} _
                   }
 
        Dim result = From s In p2  _
         Where (From s1 In p1 Where s1.Name = s.Name Select s1.Address).ToArray()(0) = _
         (From s2 In p3 Where s2.Name = s.Name Select s2.Address).ToArray()(0) Select s
        For Each item In result
            Console.WriteLine(item.Name)
        Next
        Console.ReadLine()
    End Sub
ToArray函式是LINQ To Object Framework所提供的函式,用途是將query-expression的結果轉成陣列,一旦轉成陣列後就可以透過()陣列元素存取值去取得單值。
當然,基於LINQ Expression的轉譯規則,你也可以使用傳回Boolean值的函式來協助比對。
Sub TestWhereWithFunction()
    Dim list() As String = {"code6421", "tom", "cathy"}
    Dim result = From s1 In list Where MyExpressionFunc(s1) Select s1
End Sub
 
Function MyExpressionFunc(ByVal s As String) As Boolean
     Return IIf(s = "code6421", True, False)
End Function
這個函式可以是成員函式(需要有物件、靜態函式,或是位於Module中的函式皆可。
 
Group-expression
 
 Query-expression中允許含有Group-expression,用於將資料分類用,如程式2。
[程式2]
Sub TestGroupByLinq()
        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 = From o In persons Group o By o.Address Into g = Group _
              Select New With {.Address = Address, .Persons = g}
        For Each item In result
            Console.WriteLine("Group : {0}", item.Address)
            For Each detailItem In item.Persons
                Console.WriteLine("{0}", detailItem.Name)
            Next
        Next
        Console.ReadLine()
    End Sub
此例執行結果如圖1。
[圖1]
Group : Taipai
code6421
Group : USA
jeffray
catch
Group : NY
joe
此處有兩個尚未提及的指令,Into是將Group後的結果放到g變數中,於VB.NET中,你必須指定g的值為Group關鍵字,此處Group關鍵字的內涵值為Group後的結果,是一個IEnumerable(Of Object)的物件。Select New With則是應用了VB.NET 2008的Anonymous Type技巧,建立一個簡單的物件,其中含有Address及Persons兩個屬性,Address的值來自於Address,也就是Group o By o.Address的鍵值,那就是Address的值。使用Select New With時,記得屬性名稱需以【.】作為前導字,如下:
Select New With {.Name = "code6421", .Age = 18, .Address = "Taipai"},
New With除了可放在Select之後,也可直接用來建立Anonymous Type,如下所示:
New With {.Name = "code6421", .Age = 18, .Address = "Taipai"}
而g本身是一個分類後的IEnumerable(Of Object)類型物件,其內所含的就是以Address值所分類後的Persons物件。與where-expression一樣,group-expression中也可以包含query-expression,如程式4。
[程式4]
Sub TestComplexGroupBy2()
 
        Dim p2() = { _
                   New With {.Name = "code6421", .Hand = "Manager", .Address = "Taipai"}, _
                   New With {.Name = "tom", .Hand = "Director", .Address = "Taipai"}, _
                   New With {.Name = "jeffray", .Hand = "Programmer", .Address = "NT"} _
                   }
 
        Dim result = From s In p2 Group s By Address = (From s1 In p2 Where _
            s.Name = s1.Name Select s1.Address).ToArray()(0) Into g = Group _
                     Select New With {.Address = Address, .Persons = g}
        For Each item In result
            Console.WriteLine("Group : {0}", item.Address)
            For Each detailItem In item.Persons
                Console.WriteLine("{0}", detailItem.Name)
            Next
        Next
        Console.ReadLine()
    End Sub
執行結果如圖2。
[圖2]
Group : Taipai
code6421
tom
Group : NT
jeffray
 
Join-expression
 
 LINQ Expression也支援SQL中常用的Join指令,如程式5。
[程式5]
Sub TestJoin()
        Dim p1() = { _
                     New With {.Name = "code6421", .Address = "Taipai"}, _
                     New With {.Name = "tom", .Address = "Taipai"}, _
                     New With {.Name = "jeffray", .Address = "NY"} _
                   }
        Dim p2() = { _
                     New With {.Name = "code6421", .Title = "Manager"}, _
                     New With {.Name = "tom", .Title = "Director"}, _
                     New With {.Name = "jeffray", .Title = "Programmer"} _
                   }
        Dim result = From s In p1 _
                     Join s1 In p2 On s.Name Equals s1.Name _
                     Select New With {.Name = s.Name, .Address = s.Address, .Title = s1.Title}
        For Each item In result
            Console.WriteLine("Name : {0}, Address : {1}, Title : {2}", _
                   item.Name, item.Address, item.Title)
        Next
        Console.ReadLine()
    End Sub
圖3是執行結果。
[圖3]
Name : code6421, Address : Taipai, Title : Manager
Name : tom, Address : Taipai, Title : Director
Name : jeffray, Address : NY, Title : Programmer
程式6是運用一個以上join的例子。
[程式6]
Sub TestJoin2()
        Dim p1() = { _
                     New With {.Name = "code6421", .Address = "Taipai"}, _
                     New With {.Name = "tom", .Address = "Taipai"}, _
                     New With {.Name = "jeffray", .Address = "NY"} _
                   }
        Dim p2() = { _
                     New With {.Name = "code6421", .Title = "Manager"}, _
                     New With {.Name = "tom", .Title = "Director"}, _
                     New With {.Name = "jeffray", .Title = "Programmer"} _
                   }
        Dim p3() = { _
                     New With {.Name = "code6421", .Hand = "Left"}, _
                     New With {.Name = "tom", .Hand = "Left"}, _
                     New With {.Name = "jeffray", .Hand = "Right"} _
                   }
        Dim result = From s In p1 _
                     Join s1 In p2 On s.Name Equals s1.Name _
                     Join s2 In p3 On s.Name Equals s2.Name _
                     Select New With {.Name = s.Name, .Address = s.Address, _
                          .Title = s1.Title, .Hand = s2.Hand}
        For Each item In result
            Console.WriteLine("Name : {0}, Address : {1}, Title : {2}, Hand : {3}", _
               item.Name, item.Address, item.Title, item.Hand)
        Next
        Console.ReadLine()
    End Sub
執行結果如圖4。
[圖4]
Name : code6421, Address : Taipai, Title : Manager, Hand : Left
Name : tom, Address : Taipai, Title : Director, Hand : Left
Name : jeffray, Address : NY, Title : Programmer, Hand : Right
 
Order-by-expression
 
 Order-by-expression用於將query-expression的結果集排序,如程式7。
[程式7]
Sub TestOrderLinq()
        Dim p() = {"1111", "3333", "2222"}
        Dim result = From s1 In p Order By s1 Descending Select s1
        For Each item In result
            Console.WriteLine(item)
        Next
        Console.ReadLine()
    End Sub
圖5是執行結果。
[圖5]
3333
2222
1111
Descending指的是倒序,Ascending則是正序,未加上Descending也未加上Ascending,則預設為Ascending,Order By可以有多個,彼此以 , 分接,每個都能有正倒序之修飾字,如下所示。
Sub TestOrderLinq2()
        Dim p1() = { _
                     New With {.Name = "code6421", .Address = "Taipai"}, _
                     New With {.Name = "tom", .Address = "Taipai"}, _
                     New With {.Name = "jeffray", .Address = "NY"} _
                     }
        Dim result = From s1 In p1 Order By s1.Name, s1.Address Descending Select s1
        For Each item In result
            Console.WriteLine(item.Name)
        Next
        Console.ReadLine()
    End Sub
 
 
Select-expresssion
 
 Select-expression出現於query-expression的尾端,涵意是由結果集中取出資料,其中較特別的是,有時我們會使用Select New With語句由結果集中取出需要的欄位,建構出另一個型態的回傳物件,如同於join範例中的片段。
Dim result = From s In p1 _
                     Join s1 In p2 On s.Name Equals s1.Name _
                     Join s2 In p3 On s.Name Equals s2.Name _
                     Select New With _
                   {.Name = s.Name, .Address = s.Address, .Title = s1.Title, .Hand = s2.Hand}
這是運用了VB.NET 2008的Anonymouse Type功能所達到的。
 
Distinct
 
 如同SQL中的Distinct一樣,LINQ To Object Framework中的Distinct函式允許設計師將一陣列中特定欄位值相同的部份,僅擷取出一代表值,見程式8。
[程式8]
Sub TestSelectDistinct()
        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 = (From s1 In persons Select s1.Address).Distinct()
        For Each item In result
            Console.WriteLine(item)
        Next
        Console.ReadLine()
    End Sub
此例的執行結果如圖6。
[圖6]
Taipai
USA
NY
此例僅列出一個Taipai,這就是Distinct函式的能力,相同值者僅取一代表值。
 
Select Many 功能
 
 截至目前為止的例子皆以一個query-expression作為開始,事實上LINQ Expression支援query-expression的串接,名為Select Many功能,見程式9。
[程式9]
Sub TestSelectMany()
        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 p2() = { _
                     New With {.Title = "Director"}, _
                     New With {.Title = "Programmer"}, _
                     New With {.Title = "Manager"} _
                    }
 
        Dim result = From s1 In persons _
                     From s2 In p2 _
                     Select New With {.Name = s1.Name, .Title = s2.Title}
 
        For Each item In result
            Console.WriteLine("Name : {0}, Title : {1}", item.Name, item.Title)
        Next
        Console.ReadLine()
    End Sub
此例的執行結果如圖7。
[圖7]
Name : code6421, Title : Director
Name : code6421, Title : Programmer
Name : code6421, Title : Manager
Name : jeffray, Title : Director
Name : jeffray, Title : Programmer
Name : jeffray, Title : Manager
Name : catch, Title : Director
Name : catch, Title : Programmer
Name : catch, Title : Manager
Name : joe, Title : Director
Name : joe, Title : Programmer
Name : joe, Title : Manager
 
Index
 
 LINQ To Object Framework的LINQ Expression允許指定Index,什麼是Index呢 ?請看程式10。
[程式10]
Sub TestIndex()
        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 = From s1 In persons Select s1.Name(0)
        For Each item In result
            Console.WriteLine(item)
        Next
        Console.ReadLine()
    End Sub
此例會列出陣列中Name屬性值的第一個字元,事實上這是當然的結果,因為where後面的語句,會被VB.NET編譯器當成Lambda Expression處理,而Name屬性是string型態,自然也能對其使用()陣列存取子了,同時也因為是string型別,而且會被轉為Lambda Expression,自然能呼叫屬於string型別的Contains函式了。
 
Take、TakeWhile
 
 Take函式可以讓設計師由一個IEnumerable<T>物件中取出指定的元素數量,請見程式11。
[程式11]
Sub TestTake()
        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.Take(2)
        For Each item In result
            Console.WriteLine(item.Name)
        Next
        Console.ReadLine()
    End Sub
此例會從p1中取出前兩個元素,也就是code6421及jeffray,Take函式的功能有點像是SQL Server的Select TOP。另一個TakeWhile函式則可以讓設計師以while的方式來取出IEnumerable<T>中的元素,請見程式12。
[程式12]
Sub TestTakeWhile()
        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.TakeWhile(Function(p) p.Address = "Taipai")
        For Each item In result
            Console.WriteLine(item.Name)
        Next
        Console.ReadLine()
    End Sub
此例的執行結果與Take相同,不同之處在於TakeWhile會一直取出元素,直到某個元素不符合所指定的Lambda Expression為止,這意味著假如指定Address == “USA”時,將不會有任何元素列出,因為陣列中的第一個元素就已經不符合條件了,所以While動作就結束了。
 
Skip、SkipWhile
 
 相對於Take,Skip允許設計師略過指定的元素數,如程式13。
[程式13]
Sub TestSkip()
        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 = (From s1 In persons Select s1).Skip(2)
        For Each item In result
            Console.WriteLine(item.Name)
        Next
        Console.ReadLine()
    End Sub
此例會由catch的元素開始列出。另一個SkipWhile函式與TakeWhile的概念相同,SkipWhile也是以While的觀念執行Skip動作,如程式14。
[程式14]
Sub TestSkipWhile()
        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 = (From s1 In persons Select s1).SkipWhile(Function(p) p.Address = "Taipai")
        For Each item In result
            Console.WriteLine(item.Name)
        Next
        Console.ReadLine()
    End Sub
與TakeWhile一樣,若指定Address == “USA”時,那麼將會列出所有元素,因為在第一個元素時,While就已經結束了。
 
First、FirstOrDefault
 
   First函式允許設計師指定一個Lambda Expression條件式, 她將以此對IEnumerable(Of T)中的元素查詢,並傳回第一個符合條件的元素,當不指定條件式時,First會傳回第一個元素。與Where不同,First只會傳回符合資料的第一個元素,而非內含所有符合條件元素的IEnumerable(Of T)結果集,見程式15。
[程式15]
Sub TestFirst()
        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 = (From s1 In persons Select s1).First()
        Console.WriteLine(result.Name)
        Console.ReadLine()
    End Sub
下例則是使用條件式的寫法。
Sub TestFirst2()
        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 = (From s1 In persons Select s1).First(Function(p) p.Address = "USA")
        Console.WriteLine(result.Name)
        Console.ReadLine()
    End Sub
當IEnumerable(Of T)中未含有符合條件的元素時,First將會引發例外。另一個FirstOrDefault函式與First函式類似,唯一不同之處是當IEnumerable(Of T)中未含有符合條件之元素時,她將回傳元素之型別的預設值(object為null、數值為0)。與First相對的是Last函式,可取得最後一個元素,她也有另一個同型函式為LastOrDefault,設計理念與FirstOrDefault相同。
 
ElementAt、ElementAtOrDefault
 
 ElementAt函式可以取得一個物件集中特定位置的元素,如下所示:
Sub TestElementAt()
        Dim p1() As String = {"code6421", "tom", "cathy"}
        Dim result = p1.ElementAt(1)
        Console.WriteLine(result)
        Console.ReadLine()
End Sub
此例會列出tom這個元素,當指定位置超出物件集大小時,ElementAt會拋出例外。如同FirstOrDefault一般,ElementAt有另一個同型函式:ElementAtOrDefault,當使用此函式時指定位置超出物件集大小時,不會觸發例外,而是直接回傳預設值(object為null,數值為0)。
 
ToArray、ToList、ToDictionary
 
 這三個函式會將IEnuermable(Of T)轉成Array、List或是Dictionary型別,預設情況下,當我們對某個IEnumerable(Of T)下達Where等條件式時,所取得的結果會是一個IEnumerable(Of T)物件,此時所有條件都尚未執行比對的動作,當對這個IEnumerable(Of T)物件下達MoveNext(For Each會觸發此函式)時,該物件才會進行條件比對。ToArray等函式可以改變此行為模式,當我們對IEnumerable(Of T)物件呼叫這些函式時,其將會以For Each一一巡覽IEnumerable(Of T)物件中的元素並進行條件比對,然後放到另一個結果值後傳回,這也就是說,呼叫此函式所傳回的結果值,將會是已經完成比對後的結果值,操作這個結果集自然比直接操控具條件的結果集來得有效率,程式16是一個使用ToDictionary函式的例子。
[程式16]
Sub TestToDictionary()
        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.ToDictionary(Function(p) p.Name)
        Console.WriteLine(result("code6421").Address)
        Console.ReadLine()
    End Sub
ToDictionary函式為以指定的鍵值為主鍵,將元素轉為Dictionary物件,此時你可透過預設存取子,以主鍵為參數來取得對應之元素。
 
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,但其不保證以最快的方式來完成所要求的動作。
CODESYS连接触摸屏 触摸屏:威纶通-MT8071iP(网络IP:192.168.1.10) 控制器:研华AMAX-5580(PAC)(Win10系统,网络IP:192.168.1.3) CODESYS RTE:RTE 64 V3.5 SP13 Build05 笔记本:Thinkpad L14(网络IP:192.168.1.2) CODESYS IDE:V3.5 SP13(ADD ON V3.5.13.5) 一、确认网络通讯正常(关闭防火墙) 笔记本与触摸屏网络测试情况 笔记本与控制器网络测试情况 二、控制器部分 1、新建 阅读详情

相关推荐

从零配置Archery:手把手教你用Docker部署多数据库审核系统(MySQL/PostgreSQL/ClickHouse)

本文详细介绍了如何从零开始使用Docker部署Archery多数据库审核系统,涵盖MySQL、PostgreSQL和ClickHouse的配置与整合。文章提供了生产级Docker-compose配置、审核规则定制、工单流程设置以及企业微信通知集成等实战指南,帮助团队构建统一的企业级SQL审计平台,提升数据库操作规范与安全。

e4f5g6h7的博客 918

vbLINQ实现

vb实现LINQ非常简单的例子: Dim numbers() As Integer = {1, 2, 3, 4, 5, 6, 7} Dim allNumbers = From number In numbers For Each num In allNumbers Console.WriteLine(num &a...

dj_931127的博客 568

MATLAB 控制 E4438C 信号源的研究

通过 MATLAB,我们可以实现对 E4438C 的远程控制,从而实现更高的自动化和可编程性。总结起来,通过使用 MATLAB 和 Instrument Control Toolbox,我们可以轻松地控制 E4438C 信号源并生成各种类型的信号。需要注意的是,以上代码仅为示例,实际应用中可能需要根据具体情况进行更详细的配置和错误处理。此外,MATLAB 还提供了一系列函数和命令,可用于控制 E4438C 信号源的其他功能,如频率调谐、功率设置、调制设置等。您可以根据自己的需求进行相应的配置和控制。

RswtPerl的博客 991

LINQ 首部曲 : LINQ To Object Part 1 - Using VB.NET 2008

LINQ 首部曲 : LINQ To Object Part 1 - Using VB.NET 2008 文/黃忠成  源   這一篇文章來自於我去年刊登於Run! PC雜誌上的一系列文章,原文是以C#做為語言而撰寫的,鑑於VB.NET 2008與LINQ的文章較C#來得少,特別花了時間將此系列文章一一轉換為VB.NET。 序曲: LINQ 的架構與程式語言

黄忠成 4200

C# Linq--linq to object使用

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace LinqDemo { /// <summary> /// LINQ演变 /// 场景:通过学生集合安装条件筛...

ekayun 739

【黄忠成】LINQ首部曲LINQ To Object Part 2 - Using VB.NET

這一篇一樣是發表於Run! PC雜誌,同時收錄於【極意之道-.NET Framework 3.5資料庫開發聖典-ASP.NET篇】一書中,於此我針對VB.NET做了調整。 回到起點: LINQ 語法規則 任何語...

cuibinmo3519的博客 121

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

Union Union函式可以將兩個IEnumerable(Of T)物件整合成一個, 如程式17。 [程式17] Sub TestUnion() Dim p1() As In...

cuibinmo3519的博客 126

LINQ to SQL系列Part 1 - Using LINQ to SQL

LINQ to SQL系列Part 1 - Using LINQ to SQL本文转载自:http://www.cnblogs.com/hanxianlong/archive/2007/11/16/960813.html英文原贴链接:http://weblogs.asp.net/scottgu/archive/2007/05/19/using-linq-to-sql-part-1.aspx

heker2007的专栏 1640

LINQ to SQL系列Part 9-Using a Custom LINQ Expression with the control

LINQ to SQL系列Part 9-Using a Custom LINQ Expression with the control本文英文原版:http://weblogs.asp.net/scottgu/archive/2007/09/07/linq-to-sql-part-9-using-a-custom-linq-expression-with-the-lt-asp-linqdat

heker2007的专栏 3426

Dynamic LINQ (Part 1: Using the LINQ Dynamic Query Library)

LINQ (language integrated query) is one of the new features provided with VS 2008 and .NET 3.5.  LINQ makes the concept of querying data a first class programming concept in .NET, and enables you to

张彤的专栏 1118

LINQ To XML- Part III

Some Common Operations using LINQ To XML - Part III

choudanermao的专栏 414

當VS2005 遇上 LINQ

 當VS2005 遇上 LINQ 文/黃忠成  在【極意之道-次世代 .NET Framework 3.5 資料庫開發聖典】(好長的書名,我該取個匿稱嗎?哈)一書第三章中,有下面這段敘述: 任何支援.NET Framework 2.0的程式語言都能夠叫用LINQ所提供的查詢函式,也就是

黄忠成 1万+

LINQ To SQL 與 N-Tier

 LINQ To SQL 與 N-Tier應用程式 文/黃忠成(原刊登於RUN! PC)  從發表第一篇LINQ 首部曲到現在,轉眼間LINQ系列已來到第八篇了,這是我繼2000年IntraWeb以來,於Run! PC中連載次數最多的文章了。在這長達十個月的期間,我們由最基礎的LINQ To Objects,談到了LINQ To XML及LINQ To DataSet的擴充

黄忠成 7570

WPF UI介面的革新

 透過Microsoft對Windows Vista及WPF的強力行銷,相信許多讀者對於WPF的UI能力只有驚豔二字可以形容,對於如何使用WPF來達到這些效果,基於雜誌與網路上已有相當多的文章討論,筆者於此就不再重述了,直接將主軸放在WPF的核心,也就是她是如何建構出達到這些效果的環境。WPF強大的UI介面來自於兩個關鍵技術,一是繪製控件的系統,在傳統的Windows程式中,多半是使用Wind

黄忠成 7077

LINQ To SQL與Transaction

 LINQ To SQL與Transaction 文/黃忠成   不管你是由我的書中,或是MSDN、網站處得知,LINQ to SQL之DataContext於SubmitChanges函式執行時,就算不指定Transaction,DataContext都會自動啟動一個Transaction,在許多ORM中,這算是相當常見的設計。 不過,如果我不想要這個預設的Transac

黄忠成 6500

LINQ To XML : Descendants函式

 LINQ To XML : Descendants函式     在LINQ To XML 的架構中,若想取得某個Element下的指定之子Element,可以呼叫Descendants這個Extension Method,如下所示: foreach (XElement elem in doc

黄忠成 6119

LINQ To XML And RSS

前陣子上課時展示過一個例子,以LINQ To XML來讀取RSS Feed,當時我是使用離線的XML,最近有空把他改為直接讀取網路上的RSS. 01 Sub LoadRSS() LoadRSS() 02         Dim request As System.Net.WebRequest = _ 03            System.Net.WebRequest.Crea

黄忠成 6101

LINQ To SQL 中使用In

    有讀者來信問及LINQ To SQL中如何使用IN, 倒讓我發現到我一直都未提到這個用法,^_^|||用法如下:  C#       var result = from s1 in context.Customers where (new string[]                       { "UK", "Lisboa" }).Contains(s1

黄忠成 5807

中文语料库数据集。Corpus_of_Chinese._ChineseCorpus.zip

中文语料库数据集。Corpus_of_Chinese._ChineseCorpus

C# Winform应用 常用窗体图标合集

C# Winform应用 常用窗体图标合集可以直接下下来用,量大且好看

上一篇: LINQ 首部曲 : LINQ To Object Part 1 - Using VB.NET 2008
下一篇: 好書就別放過.
Code6421
博客等级 码龄20年 259粉丝 102原创
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值