前言
"Visual Basic code-named Orcas"(Visual Basic 9.0)对基于"Visual Basic code-named Whidbey"(Visual Basic 8.0)进行了一些语言方面的扩展,以统一的方式支持以数据为中心的编程--创建,更新和查询关系数据库,XML文档和对象图。与此同时,VB9.0也引进了一些新的语言特性,来加强VB对静态类型和动态类型的支持。这些新特性包括:
开始使用Visual Basic9.0
为了体验这些语言的强大特性,我们可以从一个真实世界的例子看起--CIA World Factbook database。这个数据库包含了世界上各个国家的各种地理的,经济的,社会的和政治信息。为了我们例子的方便,我们从定义一个国家和其首都,总面积和人口入手。我们在VB9.0中使用如下类进行编程:
Class Country
Public Property Name As String
Public Property Area As Float
Public Property Population As Integer
End Class
这是一个可以用来运行例子的国家数据库的子集:
Dim Countries =
_ { new Country{ _
.Name = "Palau", .Area = 458, .Population = 16952 },_
new Country{ _
.Name = "Monaco", .Area = 1.9, .Population = 31719 },_
new Country{ _
.Name = "Belize", .Area = 22960, .Population = 219296 },_
new Country{ _
.Name = "Madagascar", .Area = 587040, .Population = 13670507 }
_ }
从这个列表中,我们可以使用如下的查询综合查询那个国家的人口少于100万:
Dim SmallCountries = Select Country _
From Country In Countries _
Where Country.Population < 1000000
For Each Country As Country In SmallCountries
Console.WriteLine(Country.Name)
Next
因为只有马达加斯加拥有100万以上的人口,因此以上程序的结果为:
PalauMonacoBelize
让我们检查一下程序,理解一下VB9.0的特性,看看是什么使得编程如此的简单。首先,定义Conuntries变量:
Dim Countries = _
{ new County { .Name = "Palau", .Area = 458, .Population = 16952 }, _
... _
}
使用新的对象初始化语法new Country {..., .Area = 458, ...}来通过一个简洁的,基于表达式的语法创建一个复杂的对象实例,这和已有的With语句有些类似。
这个声明也使用了隐式类型的局部变量声明,编译器通过该声明中等号右边的初始化表达式来推断处本地变量Countries的类型。以上的声明和一个显式类型本地变量的类型Country()的声明的效果是一样的。
Dim Countries As Country() = {...}
重复一下,这里仍然是一个强类型声明;编译器自动推断等号右边的本地声明的类型,不需要程序员手动输入类型。
本地变量声明SmallCountries使用一个SQL风格的查询综合 来过滤掉人口小于100万的国家。它本身类似于SQL,使得熟悉SQL的程序员可以很快的熟悉和使用VB的查询语法。
Dim SmallCountries = Select Country _
From Country In Countries _
Where Country.Population < 1000000
注意我们还有一个隐式类型的应用:编译器推断SmallCountries的类型为IEnumberable(Of Country)。编译器把查询综合转变为标准的查询操作。这里,转换过程可能是如下这样简单:
Function F(Country As Country) As Boolean
Return Country.Population < 1000000
End FunctionDim
SmallCountries As IEnumerable(Of Country) = _ Countries.Where(AddressOf F)
这个扩展的表达式把编译器产生的本地函数作为一个委托Addressof F传递给表达式函数Where,Where在标准的查询操作库里定义为一个IEnumerable(of T)接口扩展。
下面我们对VB9的特性做一个深入的探究。
--------------------------------------------------------------------------------
隐式类型本地变量
在一个隐式类型本地变量的声明中,本地变量的类型是从等号右边的本地声明语句的初始化表达式中推断出来的。举例来说,编译器推断如下的变量声明:
Dim Population = 31719
Dim Name = "Belize"
Dim Area = 1.9
Dim Country = New Country{ .Name = "Palau", ...}
因此它们等同于如下的显式类型声明:
Dim Population As Integer = 31719
Dim Name As String = "Belize"
Dim Area As Float = 1.9
Dim Country As Country = New Country{ .Name = "Palau", ...}
因为本地变量声明的类型缺省是通过推断的,所以怎样设置选项 Strict都没有关系,对这些变量的访问通常都是前期绑定的。在VB9.0里程序员必须显式指定后期办定,即显示声明变量类型为Object,如下:
Dim Country As Object = new Country{ .Name = "Palau", ... }
要求显式的后期绑定阻止了非正常使用后期绑定,更重要的是允许对新的数据类型比如XML使用后期绑定的强大表达功能,如下面要看到的一样。这里将会有一个可选的工程级的对当前行为的切换。
在一个For...Next或者For Each...Next中的循环控制变量也可以是一个隐式类型变量,当循环控制变量被指定的时候,如For Dim I = 0 To Count或者For Each Dim C In SmallCountries,标识符定义了一个新的隐式类型本地变量,它的类型类型是通过初始化或者集合对象表达推断的,并且受限于整个循环内。在For的右边使用Dim是VB9的一个新特性,也是作为隐式类型的循环变量存在的。
通过这样的类型推断方法,我们可以重写打印所有小国家的循环:
For Each Dim Country In SmallCountries
Console.WriteLine(Country.Name)
Next
Country的类型被推断为Country,也即SmallCountries的元素类型。
对象和集合对象初始化
在Visual Basic中,With语句简化了对具有多个成员的聚合值的访问方式,从而无需多次指定目标的表达式。在一个With的语句块中,一个对成员访问的表达式通过"."来开始,这和加上With语句中的目标表达式效果是相同的,举例来说,如下的语句初始化了一个新的Country实例并且也给它的所有域赋值:
Dim Palau = New Country()
With Palau
.Name = "Palau"
.Area = 458
.Population = 16952
End With
新的VB9.0的对象初始化采用了和With相类似的表达形式来创建复杂的对象。使用对象初始化,我们可以将上面两条语句合并为一个(隐式类型)本地声明,如下:
Dim Palau = New Country { _
.Name = "Palau",_
.Area = 458, _
.Population = 16952
}
这样的表达式方式的对象初始化对查询非常重要。典型的一个查询看上去像一个在等号右边使用Select子句初始化的一个对象声明。因为Select子句返回一个表达式,那么我们必须使用一个表达式来初始化整个对象。
就像我们已经看到的那样,对象初始化对创建集合对象式的复杂对象是非常方便的,所有的集合对象都支持一个Add方法,可以通过使用集合对象初始化表达式来实现初始化。举例来说,给定下面的城市作为局部类:
Partial Class City
Public Property Name As String
Public Property Country As String
Public Property Longitude As Float
Public Property Latitude As Float
End Class
我们可以创建一个列表List(Of City)如下:
Dim Capitals = New List(Of City){ _
{ .Name = "Antanarivo", _
.Country = "Madagascar", _
.Longitude = 47.4, _
.Lattitude = -18.6 }, _
{ .Name = "Belmopan", _
.Country = "Belize", _
.Longitude = -88.5, _
.Latitude = 17.1 }, _
{ .Name = "Monaco", _
.Country = "Monaco", _
.Longtitude = 7.2, _
.Latitude = 43.7 }, _
{ .Country = "Palau", .Name = "Koror", _
.Longitude = 135, _
.Latitude = 8 } _
}
这个例子让然使用对象初始化,初始化的构造器都是从上下文推断出来的。在这里,所有的初始化都等同于全形式的NewCity{...}。
--------------------------------------------------------------------------------
匿名类型
经常我们想从一个查询结果中移开或者取出一些类型的成员。比如说我们想取处热带国家的Name和Country,使用经纬度来指定热带区域,并且在结果中突出这些值。在VB9中,我们通过创建一个对象实例--无须对类型进行命名--对每个处于南北回归线中间的城市C:
Const TropicOfCancer = 23.5
Const TropicOfCapricorn = -23.5
Dim Tropical = Select New{ .Name = City.Name, .Country = City.Country } _
From City In Capitals _
Where TropicOfCancer =< City.Latitude _
AndAlso City.Latitude >= TropicOfCapricorn
本地变量Tropical的推断类型是一个匿名类型的实例的集合,也就是IEnumerable(Of { Name As String, Country As String })。Visual Basic编译器将会创建一个新的,系统生成的类,如_Name_As_String_Country_As_String_,它的成员名和类型都是通过对象初始化推断出来的:
Class _Name_As_String_Country_As_String_
Public Property Name As String
Public Property Country As String
Public Default Property Item(Index As Integer) As Object
...
End Class
在同一个程序里,编译器将会合并相同的匿名类型。两个指定了相同名称和类型的属性顺序的匿名对象初始化将会产生相同的匿名类型实例。形式上看,VB产生的匿名类型被转化为Object,这样允许编译器一致的将匿名类型作为参数或者函数结果传递。在VB代码里,编译器会对产生的类使用特别定制的属性进行标识,以提示类型_Name_As_String_Country_As_String_实际代表了匿名类型{ Name As String, Country As String }。
因为匿名类型一般都是用来把一个已有类型中的成员提取出来,VB9.0允许使用简单的表示方法New { City.Name, City.Country }来简写长语法的New { .Name = City.Name, .Country = City.Country }。当在一个查询综合的结果表达式中使用时,我们可以进一步简化:
Dim Tropical = Select City.Name, City.Country _
From City In Capitals _
Where TropicOfCancer =< City.Latitude _
AndAlso City.Latitude >= TropicOfCapricorn
注意长表达和短表达的效果都是一样的。
深层次的XML支持
XLinq是一个新的,常驻内存的XML编程API,它主要是为了提升最新的.NET框架的语言集成的查询框架能力而设计的。就像查询综合给标准的.NET框架查询操作增加了熟悉的方便的语法一样,Visual Basic 9.0通过XML literals和late binding over XML提供了对XLinq的支持。
为了探究XML,我们同对对平面关系数据源Countries和Capitals的查询,构造一个层次的XML模型,把每个城市的首都作为一个子元素,并且计算人口密度作为属性。
为了查找给定国家的首都,我们对每个国家的name成员和每个城市的国家名进行join操作。给定一个国家和它的首都,我们可以通过使用已经计算的值填充嵌套的表达hole轻松的构建XML段。我们可以用括号括起name属性来写"hole",如Name=(Country.Name),也可以从ASP.NET中借来一种特殊的方式,如<Name><%= City.Name %></Name>。这里是我们结合XML和查询综合的查询:
Dim CountriesWithCapital As XElement = _
<Countries> <%= Select <Country Name=(Country.Name)
Density=(Country.Population/Country.Area)>
<Capital>
<Name><%= City.Name %></Name>
<Longitude><%= City.Longitude %></Longtitude>
<Latitude><%= City.Latitude %></Latitude>
</Capital>
</Country> _
From Country In Countries, City In Capitals _
Where Country.Name = City.Country %>
</Countries>
注意XElement类型可以从声明中省略,在这里它将像其他本地声明一样进行推断。我们在这里也留下了一个显式类型,下面将解释。
在这个声明里,我们希望在<Countries>元素里Select查询的结果进行替代。因此,Select查询是第一个hole的内容,在<Countries>里使用熟悉的ASP.NET形式的标志<%=and%>来分割。因为Select查询结果是一个表达式,XML literal也是表达式,自然,Select内也可以嵌套使用Select。这样的嵌套式的使用包含了嵌套的"hole"Country.Name和计算的人口密度率Country.Population/Country.Area,还有首都的名字和