本期是Visual Basic 9.0前沿播报动态篇的第三篇,也是这个系列的完结篇。按照惯例,我将Visual Basic 9.0最激动人心的部分放在了这一篇。在这一篇,我们将看到所有新特性作用在XML上之后带来的革新性变化。
Visual Basic 9.0专门为XML集成设置了一个开发组,力求将XML语言与Visual Basic语言完全结合,使Visual Basic成为全行业对XML支持最佳的开发工具。这些新特性都基于微软的XLinq——新一代支持语言集成查询的XML框架。使用XLinq可以按照XML逻辑树状对象模型来建立和访问。本文不打算介绍XLink,有兴趣可以下载这篇文档http://msdn.microsoft.com/VBasic/Future/XLinq%20Overview.doc。我要介绍的是,VB9为XLinq所做的惊人语法。
XML字面量
你有没有想过在一种编程语言里创建XML可以达到多么简单?VB9支持直接XML文档的原始结构作为字面量,也就是说,VB9里可以直接写:
Dim x = <Books><Book author="A. S. Tanenbaum">Mordern Operating Systems</Book></Books>
这么写,VB不仅仅吧它当作一个字符串,而是可以帮你做XML结构的检查,而且编译器了解这一XML的结构,因此会有智能感知的帮助。你可以将XML字面量分成多行编写,直到遇到根元素的终结(例子中的</Books>)才判断XML字面量写完,因此无须使用续行符。这也就是说,VB的XML可以表达一个以单元素为根的XML结构片断。如果XML字面量仅仅是这样,就没有多大意思了。我们可以让这个XML字面量中的数据动态化:
Dim rootName = "Books"
Dim b As New Book {Author := "A. S. Tanenbaum", Title := "Mordern Operating Systems", ID := 20 }
Dim x = <(rootName)><Book author=(b.Author)><%= b.Title %></Book></>
我们观察一下上面的代码,元素名称Books可以用变量来指定,只要在括号组成的“洞”填入即可,相应的关闭标记则变成</>这很重要,因为元素名称已经是动态决定的了,所以引入特定写法根据位置来关闭相应标记。我们看到,attribute的值也可以用洞来填入(attribute的名称也可以用相同语法),但是标记的Inner Text部分则不能使用洞,而是要使用<%= %>这种类似于ASP.NET数据绑定的语法,这称为代码嵌入。 这已经很强大并且激动人心了,不是吗?但还不够,我们可以把查询包含的语法嵌入到XML字面量中。假设我们有一组Book要生成:
Dim books = GetBooks() '假设用别的代码生成了Book类型的集合
Dim x = _
<(rootName)>
<%= Select
<Book author=(b.Author)><%= b.Title %></Book>
From b In books Where b.ID > 5 %>
</>
我们用代码嵌入的手段,将类似于SQL的查询包含直接插入到XML字面量中去了。其结果很显然,是生成了<Books>下<Book>的列表。使用XML字面量,我们可以非常灵活地组合和生成我们想要得XML;也可以把XML直接拷贝粘贴到VB的IDE中,稍加修改而成为你自己的XML生成器。XML字面量还完善支持XML的namespace等特性。有了VB9,还需要XSLT吗?难说……
XML后期绑定
现在我们有了生成XML的手段,那么访问XML的手段呢?Visual Basic 9.0提供了XML后期绑定,我们可以用最简单的语法来访问XML。假设我们有刚才生成的Books文档,则可以用下列语法直接访问到它的某一个Book
Dim title = x.Books.Book(0).InnerText
Dim author = x.Books.Book.@author
这语法很类似于XPath,我们还可以做到XPath的//的功效:
For Each Dim book In x.Books...Book
'遍历每一个Book元素
Next
只要3个点,就可以把Books下边所有层次的Book都找到,然后直接就可以遍历。当然我们还可以用查询包含,使用SQL语法来查询XML文档。总之,在Visual Basic 9.0中使用XML会是一种前所未有的体验。
现在Visual Basic 9.0的新特性已经介绍完了,这确实是一个有着巨大进步的版本,同时特别注重用户体验的改善,保持着简单易用的特点。
在谈到动态语言与静态语言的区别时,有一句很经典的话:Static typing when possible, dynamic typing when needed。Visual Basic支持后期绑定和动态标识符,因此完全可以按照对象的运行时类型进行操作。只是后期绑定或动态标识符缺乏对参数或返回值类型的约束,仅仅按照成员的名称进行操作。举一个例子:在静态篇我们介绍过匿名类型,假设有3个匿名类型的实例(Tuple):
Dim a = New { Name := "Robbins", Age := 25 }
Dim b = New { Name := "Andrei", ID := 12, Address := "Beijing" }
Dim c = New { Name := New NameType("Mark"), Title := "PM" }
如你所见,三个Tuple具有不同的类型,由于匿名类型没有办法实现接口,因此没有办法用统一的语法操作a和b相同的属性Name。用后期绑定可以统一访问Name属性,但是c的Name属性与a,b的不同,但是后期绑定却能让他们用统一的方式访问,这就让动态的隐患扩大了。有没有既可以动态访问成员,又有部分强类型的约束呢?Visual Basic 9.0引入了动态接口。动态接口与普通接口不同,类型无需声明实现动态接口,只要具有相应动态接口定义的成员即可转换为动态接口类型。这个特性也称为鸭子类型判定。因为流传这样一种动态语言类型判断的手法:如果一个东西走路像鸭子,说话也像鸭子,那它就是鸭子。动态接口的定义需要用Dynamic修饰符:
Dynamic Interface IHasName
Property Name As String
End Interface
这个接口规定了一个String类型的属性,名为Name。任何类型的实例,只要具有String类型的属性Name,就被推定为实现IHasName:
Dim a = New { Name := "Robbins", Age := 25 }
Dim b = New Button() {Name := "Button1", Text := "Button1" }
Dim ihn As IHasName = a
ihn.Name = "Haward"
ihn = b
ihn.Name = "Button2"
尽管a的匿名类型和Button类型都没有声明实现IHasName接口,但他们都有类型为String名为Name的属性,因此都可以使用IHasName接口。动态接口提供了有类型约束和IDE智能感知的后期绑定,再次提高了Visual Basic动态编程的能力。不过要提示一点,动态接口并非类型安全性的特性。
动态语言今年来发展很快,各种脚本语言多少都带有些动态语言的特点。动态语言以其灵活的语法和丰富的运行时行为赢得了许多人的青睐。在.NET阵营,C#一直扮演着静态语言的角色,尽管3.0的语法改变很激进,但仍注重于编译时的类型检查和约束。动态特性用的不好会产生更多运行时问题,不易确保程序的可靠性,但其提高开发效率的作用是毋庸置疑的。Visual Basic 9.0是Visual Studio中唯一能让你统领动态和静态两大领域的语言,让你在程序中自由选择喜欢的风格。
Visual Basic 从1.0便支持后期绑定,即按照对象的运行时类型信息使用对象,而不是定义这个对象时的类型信息。如:
Dim l As Object = New ArrayList()
l.Add(12)
l.RemoveAt(0)
我们知道Object没有Add或RemoveAt方法,但运行时类型ArrayList具有他们,因此Visual Basic允许到运行时再检查有无相应的操作并支持。既然这个过程已经是在运行时做的了,我们就应该允许这个后期绑定的操作本身也是在运行时确定。这个语法增强叫做动态标识符。比如我们可以和这样写:
Dim l As Object = New ArrayList()
l.("Add")(12)
l.("RemoveAt")(0)
我们看到,这个时候方法名称Add和RemoveAt已经是字符串了,它当然也可以是包含字符串的变量!所以你甚至可以写出这样的代码:
l.(Console.ReadLine())(12)
l.("Remove" & "At")(0)
这样,作为方法名称的标识符就可以完全在动态期间确定了。方法名称如此,那么类型名称呢?理应如此。我们可以这样写:
Dim l As Object = New ("ArrayList")()
这样,你的变量l的类型将根据一个运行时字符串的值确定。动态标识符还能将函数的参数也动态化,假设有下列子程序:
Sub Method1(Optional arg1 As String, Optional arg2 As String)
运行的时候,我们可以动态地指定将数值赋给哪个参数:
If a > 0 Then argString = "arg1" Else argString = "arg2"
Method1({argString}:= a.ToString())
注意在:=符号之前的大括号,表示对参数名称的动态结合。有了动态标识符这一特性,Visual Basic 9.0将不需要利用反射的复杂语法,就可以做很多需要反射和运行时类型信息来做的任务,这使得Visual Basic成为解决你手边小问题的最佳帮手。本期我们已经领略到Visual Basic 9.0在动态特性上的革新,下一期将介绍第二个动态特性——动态接口。
不要惊讶,每个PDC上都会有微软最新产品的前瞻,所以在Visual Basic 2005发布以前看到Visual Basic 9.0的消息就是可以理解的了。Visual Basic 9.0 (codename Visual Basic Orcas)将随下一个Visual Stduio主版本(9.0,Orcas)一起发布,但是微软很早就开始了新语言特性的规划,在今天(PST 9月13日)召开的PDC大会上,用户不仅可以领略到新一代Visual Basic 9.0的特性,还可以在动手实验室里亲自尝试预览版。没有参加PDC大会的VB Fans和开发者,可以从我的Blog获取首轮中文播报
。注意现在距离VB9.0正式推出还很远,本系列文章仅对当前公布的内容做简单介绍,并不保证和正式版特性一致。
言归正传,这次Visual Basic 9.0的主题就是数据和动态。VB9试图从语言角度上增强以获得前所未有的数据(包括对象中的数据、数据库和XML)开发体验。其次,Visual Basic试图从动态语言中吸取一些优良的特性,让用户感到更少受限制,更方便。此外还有一些和数据无关的静态特性。我打算将本文分成动态篇和静态篇,分别介绍Visual Basic 9.0近乎于两个世界的开发体验。
我要介绍的Visual Basic 9.0第一个新特性是局部变量类型推测。在以前版本,我们声明变量的同时,必须用As语句指定变量的类型。
Dim aInt As Integer = 5
Dim aDbl As Double = 3.33
Dim aStr As String = "Hello"
Dim aLst As List(Of Integer) = anotherList.ConvertAll(Of Integer)(PStr.Length) 'VBF语法
我们观察这几个声明,他们的初始化语句都包含了足够确定返回类型的信息,比如“5”就已经确定为Integer。因此,在Visual Basic 9.0中,可以免除局部变量的类型声明,由初始化语句确定。
Dim aInt = 5
Dim aDbl = 3.33
Dim aStr = "Hello"
Dim aLst = anotherList.ConvertAll(Of Integer)(PStr.Length)
这样,变量的声明就被充分简化了。注意,这些代码似乎在Visual Basic 2005也有效,那么有什么不同呢?这里类型推测发生在编译时,是一种强类型特性。比如将aInt初始化为5,那么aInt并非是定义为Object类型,而是定义为真正的Integer,如果再给aInt赋值无法转换成Integer的数值,就会发生编译错误。在Visual Basic 2005中,Option Strict Off的情况下,不用As语句指定类型会默认为Object类型,这条规则到了Visual Basic 9.0将不再有效,要声明为Object类型必须显式指定As Object。注意最后一个,我们看到泛型类型的类型实参也会随类型推测一并确定,因此不再需要书写前思考返回类型的类型实参具体是什么了。
Visual Basic 9.0还支持For Each循环的循环变量类型自动推测。
Dim list As List(Of ALongNameStructure) = ...
For Each Dim item In list '无需写As ALongNameStructure
'使用list
Next
注意Dim语句在此帮助推测了list中的成员类型,因此你就无需手工指定它了。
为了让类型推测对数组也起作用,VB9还引入了全新简化的数组字面量,现在数组可以通过初始化自动推测自己的类型。
'VB2005 数组定义和初始化都需要指定类型
Dim aIntArr As Integer() = New Integer() {1, 2, 3}
'VB 9.0 一切都自动推测
Dim aIntArr = {1, 2, 3}
'写个括号也可以
Dim strArr() = {"a", "bb", "ccc"}
'数组的数组也可以
Dim aIntArrArr = {{1, 2}, {3, 4}}
'注意类型不统一会推测成基类,下面例子成了Object数组
Dim objArr = {1, "2", 3.1}
类型自动推测有一个限制,它只能对局部(非Static)变量进行推测,不支持对类层次的变量进行推测,即使有初始化语句也不行。
作为VB9数据新特性的基础,局部变量类型推测是一个很重要的特性,但初看其来很简单,似乎没有必要。因此你只先要记住它,当看到我后续文章的时候,你自会了解它的用处。敬请期待下一篇:对象初始化器和匿名类型。