Getting Started With LINQ in Visual Basic (翻译 + 评论)

使LINQ可行的VB9.0言特性

类型推断(自动类定)

类型推断VB传统态变量在可能的情况下、为动态变量在需要的候推测类型。在VB9里面,编译器不需要所有的数据式的声明型,而是定义变量的同,通其初始化的表达式来推测变量的型。例如下面的代

Dim population = 131000

Dim name = "Springfield"

Dim area = 1.9

Dim someNumbers = {4, 18, 11, 9, 8, 0, 5}

For Dim i = 0 To 10

     Console.WriteLine(i)

Next

将鼠量上面,你会看到量的型已被推成你所想要的型了。

Dim population As Integer = 131000
Dim name As String = "Springfield"
Dim area As Double = 1.9 
Dim someNumbers() As Integer = {4, 18, 11, 9, 8, 0, 5}

For i As Integer = 0 To 10
    Console.WriteLine(i)
Next

注意,型推侧实编译时完成的,而不是使用定。

你可以看到在此文的后部分,通过类型推你可以不用声明型而直接定义查询返回

注)

可以使用一个常量类型推断,比如

        Dim aInt = 5              ‘Integer

        Dim aDbl = 1.2            ‘Double

        Dim aStr = "farrio"       ‘String

        Dim aChr = "A"c           ‘Char

也可以声明数

        Dim aArr1() = {1, 2, 3, 4, 5}

        Dim aArr2() = {"1", " 2", "3", " 4", " 5"}

如果不是一个型的,将会尽可能的无损转可能的型。

        Dim aArr3() = {1, 2, 3, 5, 5, 6.2}      ‘Double()

但是如果数的初始化部分不能自动转化成一个型的候,将会自测为的基

    Dim aArr3() = {1, "2", 3, "5", 5, 6.2}     ‘Object()

动态只支持局部量,而不支持成员变量。

    Private a = 123       Runtime: Int32 Compile-Time: Object

匿名

VB9可以自已定量(如前例所示)。并且,他够创建一个并没有被声明型的例。因为这个新的型从来都没有被声明,所以是“匿名型”。

匿名型建立在另一个VB9的新特性的基之上——象初始化器。使用象初始化器,你可以使用一些表达式来初始化一个复杂例。例如,如果你看了Visual Basic 9 Survey工程里面Object Initializers部分的内容,你能看到一个名叫Customer,包括NameAddressCity属性。你可以用很多法来初始化例。比如,你可以完整地指定象的型以及所有成

Dim cust1 As Customer = New Customer {Name := "Nancy", _

    Address := "123 Main St.", City := "Louisville"}

似的,你也可以只初始化一部分成而其他成使用其默认值

也可以在等号的右面省略掉型来让编译器自推断型。

Dim cust1 As Customer = {Name := "Nancy", Address := "123 Main St.", City := "Louisville"}

或者你也可以省略掉等号的声明型。

Dim cust1 = New Customer {Name := "Nancy", Address := "123 Main St.", City := "Louisville"}

象初始化器和匿名合在一起,在后面的部分将会看到用到查询工程的例子。再一个简单的例子,如果你想建一个Product包含一个Name和一个Price属性,你可以定一个包含NamePriceProduct,然后像前面Customer的那个例子那样实例化,或者你也可以这样

Dim someProduct = New {Name := "paperclips", _
     Price := 1.29}

要去验证一下编译器确是按照你的要求建立了一个包含两个成例,

Console.WriteLine(someProduct.

使用智能感知技术显示所包含的两个属性(你可以看到NamePrice)。如果你将鼠标悬停在someProduct量上面,你可以看到他被定义为了一个匿名型的例。而且NamePrice被定StringDouble型。someProduct是一个强类象,在编译时就已确定了型和成,只不过编译器是通他的初始化部分来建立的。

3.73.8里面,描述基于LINQ查询表达式构的可以看到部分的内容。默认类型允你从原始型集合中建一个包含一部分成(而不是全部成)的新型集合,或者将多个集合合并在一起。使用投影,你可以选择输入数据中关心的部分并且VB将他打包在一个类里面

注)

匿名先于象初始化器。比如一个Person,包含NameAge属性。如果我

        Dim p = New {Name := "xuzy", Age := 27}

得到的不是自Person象,而是一个匿名象。

只有我在等号的一方加入Person声明(左右任何一方均可)就可以自Person例了。

另外,相同成的匿名型是相同的,比如

        Dim p1 = New {Name := "xuzy", Age := 27}

        Dim p2 = New {Name := "yinxb", Age := 25}

是同一个型的。p2 = p1操作是可以的。

匿名型也支持局部量,如果是一个成员变量,比如

    Private b = New {Name := "xuzy", Age := 27}

在运行是一个匿名型,但是编译时Object象。然可以通

        Console.WriteLine(b.Name)

来得到成,但是那时晚定了。

 

LINQ查询表达式

查询表达式的

首先看一个查询整形数的例子,来看看VB9查询

这是数组

Dim someNumbers = {4, 18, 11, 9, 8, 0, 13, 21, 5}

查询语

Dim oneDigitNumbers = From n In someNumbers _
     Where n < 10 _
     Select n

这是结果

Console.WriteLine("One-digit numbers from the list:")
For Each Dim m In oneDigitNumbers
     Console.Write(m & " ")
Next

注意在声明someNumber以及oneDigitNumbers候使用的型推

正如你所看到的,一个基本的查询表达式由From…Where…Select成。如果你将鼠标悬停在量上面,你可以看到在查询语句中,n被定义为IntegeroneDigitNumbersIEnumerable(Of Integer)型——基本上是一个数集合。

正如上面的例子所示,For…Each…Next方法可以很方便的遍(迭代)查询表达式的果集合中(或者所有实现IEnumerable接口的集合象)的一个元素。

注)

LINQ查询语句和SQL有所不用。

首先是From句,他定查询象集合并且推断出一个查询元素的型。

From n In someNumbers

someNumbers查询象集合,由于是Integer,所以n被推测为Integer型。

其次是Where子句,由于From子句里面已声明了n,所以里就可以使用了。

最后是Select子句,表示要返回的型。我可以这样

Select New {BaseValue := n, DoubleValue := n * 2}

所有的查询表达式返回都是IEnumerable接口例,只不根据Select子句的不同,他的范型型不同而起。使用动态就可以省掉用判断返回值类型的麻而直接查询结果。所以查询表达式返回值对于使用者来是透明的,我可以不用去心它。

Order By排序

想要为结果排序,在Select子句后面使用Order By

'From ... Select ... Order By ...

Dim inOrder = From n In someNumbers _
     Select n _
     Order By n

'From ... Where ... Select ... Order By ...

Dim oneDigitNumbersInOrder = From n In someNumbers _
     Where n < 10 _
     Select n _
     Order By n

Order By使用降序排序。你也可以使用AscendingDescending关键字来特指明升序是降序。

Order By n Ascending

Order By n Descending

注)

Order By字句里面行排序的字段只能是出Select里面的字段,或者是Select里面字段的某些成(属性、方法等)。一点和C# LINQ不太一。例如

        Dim oneDigitNumbers = From n In someNumbers _

            Where n < 10 _

            Select n _

            Order By n

但是如果我们这样

        Dim oneDigitNumbers = From n In someNumbers _

            Where n < 10 _

            Select n.ToString _

            Order By n

就会出编译器提示“n没有定”。不知道是不是VB LINQ的一个BUG

但是如果

        Dim oneDigitNumbers = From n In someNumbers _

            Where n < 10 _

            Select n _

            Order By n.ToString

是没有问题的。

操作符

针对的合操作符引入LINQ中,下面的例子中使用Count方法来做个例子。合操作符作用于一个集合,将一个元素合并到一个中,比如集合的数据行合数操作。

Dim oneDigitCount = Count(From n In someNumbers _
     Where n < 10 _
     Select n)

或者

Dim oneDigCt = (From n In someNumbers _
     Where n < 10 _
     Select n).Count()

Console.WriteLine("Number of one-digit numbers: " & _
     oneDigitCount)

其他的合操作符有MinMaxSumAcerage,使用方法和Count参考追加Sample Queries project101 LINQ Query SamplesAggregate Operators

注)

实际上合操作符是定System.Query.Sequence里面的一系列静方法。

展方法”的法将其展到所有IEnumerable接口的例上。我可以直接使用个静方法,也可以使用IEnumerable展方法。

分割操作符

分割操作符允你从查询返回集合中只抽取一部分,或者除了某些部分之外的子集。

使用Take(i)可以返回查询结果中的前i记录。(似于SQL里面的TOP关键字)

Dim takeFive = (From n In someNumbers _
    Select n).Take(5)

使用Skip(i)可以返回查询结果集合中前i以外的数据。

Dim skipFive = (From n In someNumbers _
    Select n _
    Order By n).Skip(5)

指定元素操作符

使用指定元素操作符,可以从查询结果中选择一个特定的元素。

First操作符返回果中的第一个元素。

Dim firstElement = (From n In someNumbers _
    Where n > 20 _
    Select n).First()

ElementAt(i)方法返回果中的第i个元素。由于果集合的下0始,所以下面的例子里面返回的是第四个元素。

Dim fourthElement = (From n In someNumbers _
    Select n _
    Order By n).ElementAt(3)

注)

要注意,指定元素操作符将会返回所迭代的型而不是IEnumerable象。

而且,如果返回的集合空集(没有元素),那使用First方法将会引一个名为ArgumentInvalidateException的异常

如果ElementAt的参数超了集合的最大下,将会返回OutOfRangeException

使用构化数据的例子

下面的例子是用了一个小的Customer例的集合,例的定可以在Visual Basic 9 Survey project里面找到。

一个Customer象包含一个Name,一个Address(街道地址)和一个City属性。有一个能返回当前客列表的方法GetCustomers,一个子程序ObjectInitializers用来建立和现实这个列表。

个例子的操作和3.1-3,5节类似,但是操作的象集合不是整形了。

首先,一个From…Where…Select的例子。查询所有地址里面包含“123”的用

' Here is the customer list
Dim customers = GetCustomers()

' Here is the query
Dim custs = From cust In customers _
     Where cust.Address.Contains("123") _
     Select cust

' Here is the result -- display their names
Console.WriteLine("Customers with 123 in their addresses:")
For Each Dim c In custs
     Console.WriteLine("  Name: " & c.Name)
Next

再次注意,编译器正确的推断了customerscustsc型。你可以使用智能感知去验证型。

才的例子是用了一个直接的投影操作,选择符合要求的元素并且返回。同的,也可以写一个单值投影来返回一个小的象,从原来的数据中只得到唯一的一个成。下面的例子返回了符合要求的元素的Name属性。

Dim custNames = From cust In customers _
     Where cust.Address.Contains("123") _
     Select cust.Name

Console.WriteLine("Customers with 123 in their addresses:")
For Each Dim cname In custNames
     Console.WriteLine("  Name: " & cname)
Next

如果你将鼠标悬停在cname量上面,你可以看到他被推测为String型。你也可以看看custscustNames型。

参看3.7投影的例子。

使用Order By可以非常简单进行排序。下面的查询语句用Name值进行排序。

Dim custsInOrder = From cust In customers _
     Where cust.Address.Contains("123") _
     Select cust _
     Order By cust.Name

Console.WriteLine("Customers with 123 addresses, in order:")
For Each Dim c In custsInOrder
     Console.WriteLine("Name: " & c.Name)
Next

你也可以使用连续值进行排序。试试这个,先用City排序再用Name排序。

(注意,不能Select以外的象作排序的字段。也即是,我目前无法投影cust.Name,并且用cust.City排序。)

Dim custsInOrder = From cust In customers _
     Select cust _
     Order By cust.City,cust.Name

下面例子里面的合操作符和简单数据的使用方法、果一。当然你首先要决定哪些象要被比、合或者平均。

Dim firstCity = (From cust In customers _
     Select cust.City).Min()
' or alternatively
'Dim firstCity = Min (From cust In customers _
' Select cust.City)

Console.WriteLine ("The first city, alphabetically, is " & _
     firstCity)

下面的个例子得到了City属性集合并且取其中的第一个

Dim startCity = (From cust In customers _
    Select cust.City).First()

个例子取得除了第一个以外的其它

Dim allButFirst = (From cust In customers _
    Select cust.City).Skip(1)

最后,个例子用了ElementAt取返回集合中的第二个元素。

Dim secondCity = (From cust In customers _
    Select cust.City).ElementAt(1)

使用匿名行多投影

匿名型可以用来从行多投影操作(参照3.6单值投影,以及2.2的匿名型介)。例如,在Visual Basic 9 Survey例子工程里面,你可能希望从Customer里面得到一个包含街道地址和城市的例列表,但是不包含Name信息。使用匿名型你可以有很多的途径来实现

建立一个附名字的匿名型。(Customer里面的AddressCity在匿名型里面建立两个新的属性名字)

Dim customerLocs = From cust In customers _

     Select New {theStreet := cust.Address, theCity := cust.City}

所属的属性作匿名的的属性。(参3.8

Dim customerLocs = From cust In customers _
     Select New {cust.Address, cust.City}

最后,用一个短的查询表达式实现

Dim customerLocs = From cust In customers _
     Select cust.Address, cust.City

下面的例子里面,想要示在customerLocs列表里面cLoc.Name属性将会引发错误,因为这个成不属于返回的元素的。但是cLoc.AddresscLoc.City仍然有效。

Console.WriteLine("City names in customerLocs: ")
For Each Dim cLoc In customerLocs
     'Console.WriteLine(cLoc.Name)
     Console.WriteLine("  " & cLoc.City)
Next

下面的例子更能一特性,一个查询当前程的程序。System.Diagnostics.Process里面多的公共属性,也你只ProcessNameID两个信息。如果是这样,你就可以建立一个只包含着两个成的新集合。

Dim tasks = From proc In _
   System.Diagnostics.Process.GetProcesses() _
     Where proc.Threads.Count > 10 _
     Select proc.ProcessName, proc.ID

Console.WriteLine("Processes:")
For Each Dim task In tasks
     Console.WriteLine("  Name : " & task.ProcessName & _
        " ID: " & task.ID)
Next

使用只能感知去检测Task.ProcessName,他是一个String型属性,IDInteger型,并且Task是一个匿名型。个例子突出了一个新的信息:你可以查询所有的集合型。GetProcesses方法返回一个程集合象,你可以想查询任何其它集合象一样查询他。

注)

如前所述,Order By子句只能查询Select子句里面的目,如果Select子句是一个匿名型,那将无法Order By操作。但是在C# 3.0里面是可以的。

使用it关键字(参3.9)可以解决问题It关键字表示查询操作的迭代器象,也就是我要返回的果集合的成员类型。例如

        Dim ps = From p In persions _

            Select p.Name, p.Age _

            Order By it.Age Descending, it.Name Ascending

里的it就是Select子句返回的匿名型。

从多个数据源合数据

你可以使用LINQ查询表达式方便的将多个数据源的数据合在一起,运用起来就和从一数据源取得数据一简单。下面的例子将所有customers数据里面,地址信息包含在一个oneDigitNumbers(在3.1里面建的)里面的成取出来。

Dim custsWMatchingDigit = From cust In customers, n In oneDigitNumbers _
    Where cust.Address.Contains(n.ToString()) _
    Select cust

Console.WriteLine("Customers with digit match in their addresses:")
For Each Dim c In custsWMatchingDigit
    Console.WriteLine("  " & c.Name)
Next

你也可以方便的再返回信息里面加入另一个数据源的内容。

Dim custsWMatchingDigit = From cust In customers, n In oneDigitNumbers _
    Where cust.Address.Contains(n.ToString()) _
    Select cust,n

Console.WriteLine("Customers with digit match in their addresses:")
For Each Dim custn In custsWMatchingDigit
    Console.WriteLine("  " & custn.cust.Name & ", " & custn.n)
Next

注意,Name属性在两个示操作里面的代是不一的,因custsWMatchingDigit在两个例子里面是两个不同的构。第一个例子返回的是customer的集合,包含AddressCity属性,但是第二个例子里面,返回的却是一个包含了custn属性的匿名型。

再上一个例子里面,oneDigitNumberscustomers实现IEnumerable接口,但并不是必需的。如果你用someNumbers,一个整数数来替oneDigitNumbers,程序仍旧能正常运行。

Form子句里面有一个自的迭代序。上一个例子是通一个数列表内的customer的地址信息索。下面的例子,通所有的customer的地址和整数列表索。然得到果列表的成是一的,但是序却是不一的。

Dim digitsWMatchingCusts = From n In oneDigitNumbers, cust In customers _
    Where cust.Address.Contains(n.ToString()) _
    Select cust

Console.WriteLine("With the iterator for digits first:")
For Each Dim c In digitsWMatchingCusts
    Console.WriteLine("  " & c.Name)
Next

注)

使用上面的索,将会生笛卡。相同的目将会重。比如在number里面有1,2,3几个成,在customeraddress中有一个address包含了1,2,3,那将会在果集合中返回三个customer象。

但是使用下面的方法可以避免象。

首先考虑这样两个PersonDepartment

Public Class Department

    Private m_dCode As String

    Private m_dName As String

    Public Property Code() As String

        Get

            Return Me.m_dCode

        End Get

        Set(ByVal value As String)

            Me.m_dCode = value

        End Set

    End Property

    Public Property Name() As String

        Get

            Return Me.m_dName

        End Get

        Set(ByVal value As String)

            Me.m_dName = value

        End Set

    End Property

End Class

 

Public Class Person

    Private m_Name As String

    Private m_Age As Integer

    Private m_Department As String

    Public Property Department() As String

        Get

            Return Me.m_Department

        End Get

        Set(ByVal value As String)

            Me.m_Department = value

        End Set

    End Property

    Public Property Name() As String

        Get

            Return Me.m_Name

        End Get

        Set(ByVal value As String)

            Me.m_Name = value

        End Set

    End Property

    Public Property Age() As Integer

        Get

            Return Me.m_Age

        End Get

        Set(ByVal value As Integer)

            Me.m_Age = value

        End Set

    End Property

    Public Overrides Function ToString() As String

        Return String.Format("Name: {0}{1}Age: {2}{1}Department: {3}", Me.m_Name, vbTab, Me.m_Age, Me.m_Department)

    End Function

End Class

以及他得到数的方法

Function GetPersonList() As Person()

    Return New Person() { _

    New Person {Name := "xuzy", Age := 27, Department := "1"}, _

    New Person {Name := "zhangkun", Age := 24, Department := "QA"}, _

    New Person {Name := "yinxb", Age := 25, Department := "1"}, _

    New Person {Name := "Diablo", Age := 30, Department := "1"}, _

    New Person {Name := "hehui", Age := 26, Department := "1"}, _

    New Person {Name := "Happy2007", Age := 29, Department := "3"}, _

    New Person {Name := "loulou", Age := 29, Department := "1"}, _

    New Person {Name := "pangzi", Age := 29, Department := "1"}}

End Function

 

Function GetDepartmentList() As Department()

    Return New Department() { _

    New Department {Code := "1", Name := "1"}, _

    New Department {Code := "2", Name := "2"}, _

    New Department {Code := "3", Name := "3"}, _

    New Department {Code := "QA", Name := "QA"}}

End Function

实际的操作如下

    Dim persions = GetPersonList()

    Dim dps = GetDepartmentList()

    Dim ps = From d In dps, p In persions _

        Where p.Department = d.Code _

        Select New {Person := p.Name, Age := p.Age, Code := p.Department, Dp := d.Name}

    For Each p In ps

        Console.WriteLine(p.ToString)

    Next

实际上是一个左右接的例子,通Where p.Department = d.Code接两个数据源,如果不匹配出。这样就消除了笛卡儿

操作

Gourp By你使用某一个对输入集合的元素行分,并且允访问每一个子在的Group By使用一个表示迭代器量的上下文关键it。由Groupt By子句,it关键字表示查询结果集合中的迭代元素。例如,下面的查询someNumbers组为奇数和偶数,并且为每一个组记数。

Dim oddEven = From n In someNumbers _
     Group By n Mod 2 _
     Select it.Key, Count(it)

Console.WriteLine("Count of even and odd integers in someNumbers:")
For Each Dim g In oddEven
     Console.WriteLine("Key: " & g.Key & " Count: " & g.Count)
Next
Console.WriteLine()

示的果如下:

Key: 0  Count: 4

Key: 1  Count: 5

Key目也可以不是数字。下面的例子将customers按照city行分KeyCity名字。(字符串)

Dim cityCount = From cust In customers _
    Group By cust.City _
    Select it.Key, Count(it)

Console.WriteLine("Number of times each city occurs in customers:")
For Each Dim ct in cityCount
    Console.WriteLine(ct.Key & ", " & ct.Count)
Next
Console.WriteLine()

注意,出分信息的cities序和序保持一致。如果你想排序,可以在查询表达式的后面使用Order By子句。

Dim cityCount = From cust In customers _
    Group By cust.City _
    Select it.Key, Count(it) _
    Order By it.Key

Key也可以是一个匿名型。下面的例子展了3.8的例子,从customer里面选择address属性中包含oneDigitNumbers整形数元素,使用一个包含了CityName属性的匿名型来分,最后将里面有多少成员进行投影。

Dim grp = From c In customers, n In oneDigitNumbers _
    Where c.Address.Contains(n.ToString()) _
    Group By New {c.City, c.Name} _
    Select it.key, Count(it)
    Order By it.key.City

Console.WriteLine("Results with anonymous type:")
For Each Dim g In grp
    Console.WriteLine("  Key: {0}, Occurrences: {1}", g.key, g.Count)
Next

注)

实际上,包含了Group By子句的查询表达式的返回是一个IGrouping接口的例,而且它还实现IEnumerable接口。考下面的代

        Dim nums = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

        Dim odd = From n In nums _

            Group By n Mod 2 _

            Select it

里,it返回的迭代元素就是IGrouping。通

        For Each Dim g In odd

            Console.WriteLine("Key: " & g.Key)

        Next

就可以将所有的的信息表示出来。(IGrouping里面只有一个Key属性,它返回当前的分组关键型的对应值。)

们还可以得到下面的内容,比如

        For Each Dim g In odd

            Console.WriteLine("Key: " & g.Key)

            For Each Dim _g In g

                Console.WriteLine(_g)

            Next

        Next

这时候,_g型就是Integer了。我可以将分操作看成是两集合的嵌套,外是所有得到的足的集合,内里面包含的元数据的集合。

 

posted @ 2007-02-13 16:14  妖居  阅读(2611)  评论(9编辑  收藏  举报