技术开发 频道

老树新芽 体验Visual Basic 9.0新功能(2)

【IT168 技术文档】

  匿名类型

  经常我们想从一个查询结果中移开或者取出一些类型的成员。比如说我们想取处热带国家的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,还有首都的名字和坐标。
 
  当编译运行时,上面的查询将会返回如下的XML文档:
<Countries>  <Country Name="Palau" Density="0.037117903930131008">   <Capital>    <Name>Koror</Name>    <Longitude>135</Longitude>    <Latitude>8</Latitude>   </Capital>  </Country>  <Country Name="Monaco" Density="16694.21052631579">   <Capital>    <Name>Monaco</Name>    <Longitude>7.2</Longitude>    <Latitude>3.7</Latitude>   </Capital>  </Country>  <Country Name="Belize" Density="9.5512195121951216">   <Capital>    <Name>Belmopan</Name>    <Longitude>-88.5</Longitude>    <Latitude>17.1</Latitude>   </Capital>  </Country>  <Country Name="Madagascar" Density="23.287181452711909">   <Capital>    <Name>Antananarivo</Name>    <Longitude>47.4</Longitude>    <Latitude>-18.6</Latitude>   </Capital>  </Country> </Countries>
  
   Visual Basic 9.0编译XML literal成System.Xml.XLinq对象,保证其他使用XLinq的语言和VB一样具有互操作性。在我们这个例子里,编译器产生的代码(如果我们可以看到的话)可能是:
Dim CountriesWithCapital As XElement = _    New XElement("Countries", _     Select New XElement("Country", _      New XAttribute("Name", Country.Name), _     New XAttribute("Density", Country.Population/Country.Area), _     New XElement("Capital", _     New XElement("Name", City.Name), _     New XElement("Longitude", City.Longitude), _     New XElement("Latitude", City.Latitude)     )    )    From Country In Countries, City In Capitals _    Where Country.Name = City.Country)

 

 

  除了构造XML,Visual Basic9.0也通过XML的后期绑定简化了访问XML结构。也就是,在VB代码中的标识符是在运行时和XML属性和元素绑定的。举例来说,我们可以打印出人口密度如下:
 
  1. 使用child axis CountriesWithCapital.Country来从CountriesWithCapital XML结构获取所有的"Country"元素;
 
  2. 使用attribute axis Country.@Density获取Country元素的"Density"属性;
 
  3. 使用descendants axis Country...Latitude--在源代码中写三个点,获取Country元素的所有"Latitude"子元素,无论他们在层次结构中处于多深;
 
  4. 使用extension indexer on IEnumerable(Of T)来选择结果序列中的第一个元素。

  如果放在一起,代码如下:

For Each Dim Country In CountriesWithCapital.Country  Console.WriteLine("Density = "+ Country.@Density)  Console.WriteLine("Latitude = "+ Country...Latitude(0)) Next
  
  当一个声明,赋值或者初始化是Object类型而不是其他指定的类型的时候,编译器知道对一般的对象使用后期绑定。同时,编译器液汁到当目标表达式是XElement, XDocument, or XAttribute的类型或者集合对象时使用后期绑定。
 
  作为后期绑定的结果,编译器将如下翻译:
 
  ·child axis表达式CountriesWithCapital.Country 翻译成raw XLinq call CountriesWithCapital.Elements("Country"),返回所有名为"Country"的Country元素的子元素的集合对象;
 
  ·attribute axis 表达式Country.@Density 翻译成Country.Attribute("Density"),返回Country的名为"Density"的单个子属性;  

  ·descendant axis表达式Country...Latitude(0)翻译成ElementAt(Country.Descendants(Latitude),0)的结合,返回所有Country任何层次的所有元素的集合对象。

 

0
相关文章