随着VS2010早期CTP的放出,大家对下一代开发系统的期待热情又一次掀起了。除了已经开始被大家尝鲜的C#4以外,Visual Basic也进化成了10.0。不过据我观察,VB10的改变还没有C#4那么明朗,我们目前的确可以看到一些语法上的变化,然而似乎VB10并未真正接近完工的地步,还有很多特性有待继续开发。大家也许还记得VB9引入的令人眼前一亮的XML集成语法,使得VB拥有了业界最为紧密的XML集成。但VB需要编译才能使用,这一点阻碍了VB强大的XML处理能力和动态能力,如果VB变成一个完全动态的编程语言,包括脚本化运行的能力,那么VB使用方便的特点将再一次发挥出来。但是VS2010 CTP中尚未看到基于新DLR的VB编译器。我们能看到的仍然是一个静态编译的VB10。所以我这次主要介绍一下VB语法方面的改进,更多改进等到VS2010来临之际自然会知晓。
1. 隐式续行符
用过VB的人都知道VB的语句是写成一行一行的,无需分号隔离。一般情况下语句只能写成一行,不能换行。而从VB5.0开始,如果觉得语句写成一行太长,可以加入续行符(line continuation)来换行。比如说:
<HideModuleName()> _ Module Module1
<STAThread()> _ Sub Main() Dim message = "Your directory is" & _ Environment.CurrentDirectory()
Dim l As New List(Of Integer) Dim r = From item In l _ Where item > 0 AndAlso item < 100 _ Select Number = item.ToString() End Sub
Function Foo(ByVal x As Integer, _ ByVal y As String, _ ByVal z As Double) As Single End Function End Module |
我们可以看到这段代码中多处出现了续行符(就是一个空格加一个下划线)。无论是Attribute应用语法,还是表达式、Linq表达式或函数签名,都必须用续行符来换行。
首先我们说换行并非必须,而仅仅是为了美观。像Linq这样的语法出现之后,换行书写就成了很多人的迫切需要。因此VB10改进了语法,引入了隐式换行符的概念。当VB能够判断一行语法未完的上下文,就会自动将紧接的一行视为续行,而无需显示加入续行符。这样一来在常见的场合,你就可以任意换行,充分发扬Basic语言的自由风格了:
<HideModuleName()> Module Module1
<STAThread()> Sub Main() Dim message = "Your directory is" & Environment.CurrentDirectory()
Dim l As New List(Of Integer) Dim r = From item In l Where item > 0 AndAlso item < 100 Select Number = item.ToString() End Sub
Function Foo(ByVal x As Integer, ByVal y As String, ByVal z As Double) As Single End Function End Module |
这样一来清爽多了。不过隐式续行符和显式续行符一样,有个小小的缺陷,就是不能在后面插入注释。据说这是VB IDE团队过分依赖于此特性,以至于超多历史代码无法一一改变造成的……
2. 多行Lambda表达式和方法型Lambda表达式
VB2008为了支持Linq已经引入了Lambda表达式的概念。但VB2008的Lambda表达式有局限性,只能是函数形态(必须有返回值),同时最多只能含有一个表达式,所以无法加入多行语句。虽然函数式编程仅支持一行语句也可编写,但若能支持多行语句就能扩大与VB传统语法的结合能力。比如说可以用在PLinq等场合。语法非常简单,就如同是将Sub和Function写在了方法体内部一样:
Dim f = Function(x) x + 1 '函数形态Lambda Dim g = Sub(x) Console.WriteLine(x) '方法形态Lambda Dim h = Function(x, y) '多行语句函数 Dim z = x`+ y Return 2 * Math.Squr(Z) End Function
Dim i = Sub() '多行语句子程序 f(1) g(2) h(3, 4) End Sub |
多行Lambda和语句型Lambda都可以捕获局部变量,完成函数式的诸多算法。他们同样也基于匿名委托工作。有了此工具,就可以更有效地在VB中进行函数式风格的编程了。
3. 数组、集合和字典的初始化语句。
这个新特性是VB9日程过于紧迫而被砍掉的特性,其实和C#的类似功能是一样的。首先,VB10引入了一个纯粹的数组字面量。无需任何修饰,下列语法就表示一个强类型数组:
{1, 2, 3, 4} |
编译器会自动推测它是Integer类型的数组。这个表达式不仅仅可以用于赋值,还可以直接出现在需要数组的上下文中。这是个很好的特性,基于一些Linq操作,VB也可以像函数式语言操作“表”一样,在数组上进行一些眼花缭乱的算法了。同时,List(Of T)和Dictionary(Of TKey, TValue)类型也可以用类似的表达式初始化。暂定关键字为From:
Dim l As New List(Of Integer) From {1, 2, 3, 4} Dim d As New Dictionary(Of String, Double) From {{"A", 1}, {"B", 2}} |
其实我个人感觉字典的初始化语法应该像JSON语法那样才好。。。
4. 泛型接口和泛型委托的协变、反变性
我们都知道.NET泛型是不允许任何形式的协变或反变的。也就是说Foo(Of T)是不可能转成Foo(Of U)的,不管T和U之间有什么样的关系。用过Java泛型的人可能会对此表示疑问。但其实仔细一想不难发现,泛型参数T在这个类型中如何被使用我们是不能加以约束的。因此T既可能出现在成员函数的参数上,也可能出现在返回值上。因此允许协变必然会导致某种类型不安全的现象发生。现在CLR增加了一种新的描述泛型类型参数的方法,可以限定类型参数只能用于方法的参数或返回值。这样就使得安全的协变成为可能。VB10为此提供了两种语法(仅能用于声明泛型接口和泛型委托,不能用于泛型类或泛型方法):
Interface IFoo(Of In T) Sub Foo(ByVal x As T) End Interface
Interface IBar(Of Out T) Function Bar() As T End Interface |
其中被In修饰的泛型类型参数仅能在接口或委托中用于函数的参数类型,而被Out修饰的泛型类型参数则只能用于返回值类型。
加上这样的修饰之后,该接口就允许某些条件下的协变或反变了。首先,IFoo(Of In T)将可以反变成IFoo(Of In U),当且仅当U继承自T。比方说IComparable(Of Object)将能够反变成IComparable(Of String)。因为IComparable(Of In T)拥有In的修饰。接下来IBar(Of Out T)也将可以协变成IBar(Of Out U),当且仅当T继承自U。比如IEnumerable(Of String)将能够协变成IEnumerable(Of Object)。注意达成协变和反变的条件仅仅是继承关系,而且必须都是引用类型。值类型泛型和引用类型泛型因为有不同的JIT已构建类型(Constructed Type),所以是不能运行时协变的。
综上所述,VB10目前放出来的主要改进就是一些语法方面的完善工作。VB从很久以前就已经为动态大潮做好了准备,C#4的大部分新特性(例如dynamic,可选参数,命名参数)全部都是10年前VB就已经拥有的。现在VB将传统的动态能力与新的DLR结合在一起,加上XML语法的卓越威力,必将成为.NET 4时代重要的动态语言。后续新特性,待官方发布后将继续研究和报道。