摘要: 本文探讨 Visual Basic 2005 中的许多新的增强功能,并提供简单的代码片段来阐释它们中有多少起作用。增强的功能有 XML 注释、泛型、Global 关键字、部分类型、My 等。

本文和代码示例基于 Visual Studio 2005 的预发布版本(以前代号为“Whidbey”)。文中包含的所有信息均有可能变更。


简介

Visual Studio 2005 公开测试版提供了稳定的 Visual Basic 2005 预览,其中的新功能已经固定下来,在 RTM 版本之前不会再有多大变化。当然,有些东西还是会变,但是可以相信,最终产品会与 Beta 1 预览版很相似。即便如此,我还是不建议您在用于生产的机器中安装测试版,而应该运行于安全的 Microsoft Virtual PC 映像中。

对 Visual Basic 语言的主要更改有 My、XML 注释和泛型。还有几个新的语言语句弥补了逻辑缺口,包括 UsingContinueTryCast,以及 Global 关键字。有大量的结构改进,包括对属性访问器可访问性、自定义事件访问器、部分类型和应用程序级事件的更改。运算符重载和转换运算符,同时还有 IsNot 运算符,都是全新的。其他有用的更改包括无符号类型、窗体默认实例、编译器警告和显式数组界限。

首次启动 Visual Studio 2005 时,会提示您选择您所喜欢的 IDE 设置。这些设置控制代码编辑器中的一些功能,例如,IDE 键映射、窗口布局和代码格式化。您可以随时使用 Tools | Import/Export Settings 菜单项来更改设置,如图 1 所示。可以为另一种编程语言导入默认设置,或者导入公司中所有开发人员共同使用的自定义设置文件。


图 1. 导入自定义设置以标准化公司中的开发环境

My

开发人员从 Visual Basic 6.0 及更早版本移植到 .NET 所面临的一个主要挑战是,.NET Framework 及其命名空间层次结构中类的数量极其庞大。常用的应用程序和环境对象可以在 .NET Framework 的基类库 (BCL) 中找到,但掌握它们的位置是比较费时的。Visual Basic 2005 中新的 My 层次结构对许多常用的框架类提供了便利的、从逻辑上加以组织的访问,从而加速并简化了应用程序开发。

My 带来了两样好东西:

向 Framework 类提供组织良好的快捷方式的项。

在向项目添加窗体、设置、资源和 Web 服务时创建的动态对象集。

My 快捷方式从本质上讲是一个快速拨入常用 BCL 对象的方式。顶层 My 快捷方式是 My.Application、My.Computer 和 My.User,其中每一个都包含自己的对象层次结构。例如,My.User 对象公开 AudioFileSystemKeyboardMouseNetwork、Registry 等:

Dim sVar As String
sVar = My.User.Identity.Name
sVar = My.Computer.Info.OSFullName
sVar = My.Application.CommandLineArgs.Count.ToString

动态的 My 对象是您在向项目添加窗体、资源、设置和 Web 服务时由 Visual Basic 自动填充的。My.Resources 和 My.Settings 特别有趣,因为当您向项目添加资源和设置时,Visual Basic 编译器会生成强类型化变量引用。

在解决方案资源管理器中双击 My Project 项目,以便调出应用程序设计器。单击左侧选项卡列表中的 Settings,如图 2 所示。


图 2. 显示 Settings 选项卡的应用程序设计器。Error List 窗格显示错误、警告和消息,而且已从 Task List 中独立出来。

添加一个设置名,比如在 User 范围内添加一个整型的 MaxInputValue,将值设为 100。可以立即用代码中的强类型化对象和完整的 IntelliSense 引用该对象,如图 3 所示。

languageenhancements-fig3

图 3. 针对在 Settings 应用程序设计器中创建的设置值的 IntelliSense 弹出窗口示例

Settings 设计器的右上角是一个标记为 View Code 的链接。单击该链接可以查看名为 MySettings_user.vb 的文件。这个文件是一个部分类,可以在其中添加代码来处理设置事件,以便当设置更改时更新应用程序状态(要获得更多信息,请参阅关于部分类型的部分):

Partial Public Class MySettings
    '...
End Class

这里公开了三个 MySettings 事件:

PropertyChanged

SettingChanging

SettingsSaving

该应用程序 Settings 接口是可插接的,这意味着您可以通过实现这些事件例程来定义设置的保存方式。

您可以通过添加到项目中的 Resources 来获得相同的强类型化。如果您在 Resources 设计器的 Images 区域放置一个名为 Happy.bmp 的图像,则可以在代码中以 My.Resources.Happy 来引用它。Happy 是 Bitmap 类型,这意味着不需要从 Object 或 Image 类型进行强制转换就可以在代码中使用该位图。当您在代码中键入“Happy”时,IntelliSense 也会立即生效。例如:

PictureBox1.Image = My.Resources.HAPPY

这行代码将该位图对象分配给 PictureBox 的 Image 属性。可以看到图 4 中的 IntelliSense 弹出窗口,它指示该对象是 Bitmap 类型。


图 4. 在应用程序设计器的 Resources 选项卡上拖放位图图像可以产生能在代码中立即使用的强类型化对象。

当您在项目中添加窗体和 Web 服务时,My 对象就会由这些可在代码中引用的对象的强类型化默认实例来动态填充。

可以看到,通过 My 可以更为方便地查找与应用程序和环境相关的 BCL 类,同时为您提供对添加到项目中的设置、资源和其他对象的即时、强类型化访问。对于任何经验级的 Visual Basic 开发人员,这都能够极大提高他们的生产效率。

XML 注释

XML 注释允许您向代码添加结构化的文档,由于 C# 首先做到了这一点,因此这令 Visual Basic 开发人员羡慕不已。XML 注释可以描述各种代码元素,包括类、字段、方法和枚举。

一旦创建了描述代码功能的 XML 注释,当您在编辑器中键入代码功能时,就可以立即在参数和返回类型中获得 IntelliSense。假定您有一个具有以下签名的函数原型:

Private Function GetCustomerData(ByVal CustomerCode As String) _
    As DataSet

将代码插入点放在函数声明上面一行,并键入三个单引号字符。Visual Basic 会生成一个匹配该函数声明的 XML 注释模板,可以按 TAB 键浏览,并像填充窗体那样填充它。假设您为函数填充的注释模板如下所示:

'''<summary>  
''' Returns a DataSet for a single customer.
''' </summary>
''' <param name="CustomerCode">Customer Code as String.</param>
''' <returns>DataSet for the supplied CustomerCode.</returns>
''' <remarks>Resides in the application's data layer.</remarks>

当您在代码的其他位置键入该函数名时,就可以获得具有该函数摘要、参数和返回值的完整 IntelliSense,如图 5 所示。

languageenhancements-fig5

图 5. 这个工具提示显示了向代码元素添加 XML 注释时获得的即时 IntelliSense 反馈。

这是协作和团队环境下的一个主要功能。可以从插入在源文件中的结构化 XML 注释中生成代码文档。另外,应用程序架构师或开发人员负责人可以在实现之前先对功能进行原型化和注释。当开发人员编写使用原型化对象的代码时,IntelliSense 就会指导他们正确使用这些功能。

XML 注释也会被编译器视为代码的一个完整部分。注释标记值用颜色标示为注释,如果折叠 XML 注释上的大纲,则在整个 XML 注释模板的位置只显示摘要标记值(请参见图 6)。


图 6. XML 注释与编译器和代码窗口完全集成。FillDataGrid 函数上的 XML 注释折叠到它的摘要行中。

如果在函数原型中将 CustomerCode 参数的名称改为 CustomerID,则编译器会在<param name="CustomerCode"> 标记下面添加一个绿色波纹线,用于警告这两个名字没有匹配(要了解更多内容,请参阅编译器警告部分)。

当您构建项目时,Visual Basic 会自动生成一个 XML 文档文件。这个文件就是出现在应用程序输出目录中的 AssemblyName.xml。因为这个文件为 XML 格式,所以很容易根据需要将它转换成其他输出格式。XML 注释使得为代码生成文档更加轻松简便,因为它在您进行编码时可以作为 IntelliSense,而在您构建应用程序时可以作为代码文档。

泛型

BCL 提供了 System.Collections.Generic 命名空间,它包含几个用于定义泛型集合的类。它们之所以称为泛型,是因为您在声明时为包含的对象指定的是一个类型占位符,而不是特定的类型。只有在创建该集合的实例时,才会为所存储的对象指定一个特定类型。

泛型极大地节省了时间。如果您曾经创建过自定义集合,就会知道它包含非常多的代码。Visual Basic 中的泛型可以让您在一行代码中创建相同的自定义集合。不再需要为想要存储的每类对象创建独立的自定义集合。只要在实例化泛型集合时提供需要的类型即可。

了解泛型工作方式的最简单办法是通过示例来了解。假定您有一个简单的 Customer 类,它有两个公共属性(可以简单地视为公共变量):

Public Class Customer
    Public Name As String
    Public CreditLimit As Decimal

    Public Sub New(ByVal CustomerName As String, _
      ByVal CustCreditLimit As Decimal)
        Name = CustomerName
        CreditLimit = CustCreditLimit
    End Sub
End Class

可以使用泛型集合 List 类来声明一个 Customer 列表,如下所示:

Dim Customers As New System.Collections.Generic.List(Of Customer)

通过这行代码,可以声明一个强类型化列表,它只存储 Customer 类型,并会在包含的 Customer 对象中为您提供完整的 IntelliSense。可以轻松地创建 ProductOrder 对象列表,不需要为每种类型编写所有的自定义集合代码就可以有效地创建一组自定义集合。现在,您可以编写如下所示的代码:

Dim custA As New Customer("CustA", 1000)
Customers.Add(custA)
Dim custB As New Customer("CustB", 2000)
Customers.Add(custB)
For Each c As Customer In Customers
    MessageBox.Show("Customer: " & c.Name & _
      ", limit: " & c.CreditLimit)
Next

'compile error: Product cannot be converted to Customer
Dim prodA As New Product("ProdA", CDec(29.95))
Customers.Add(prodA) 

也可以使用泛型集合来获得较好的性能,因为存储的对象是强类型化的,而且在内部没有保留为 Object 类型。

在 System.Collections.Generic 命名空间中还有其他可用的集合泛型。例如,Dictionary(of K,V) 集合允许您为键和值指定类型。LinkedList(of T) 和 Stack(of T) 泛型集合就像正常链接的列表和堆栈,不同的是由您指定它们包含哪些类型的对象。

您可以使用新的泛型类型声明语法来创建自己的泛型类型。请考虑可以让您比较不同种对象的 Comparer 类:

Public Class Comparer(Of itemType)
    '...
End Class

您可以在以逗号分隔的列表中定义多个类型占位符。您也可以定义约束来限制在实例化泛型时向泛型提供哪些类:

Public Class Comparer(Of itemType As IComparable)
    '...
End Class

这个约束确保 Comparer(of T) 实例只能由实现了 IComparable 接口的类来创建。也可以对类声明应用多个约束。

作为例子,请考虑实现了 IComparable 的扩展 Customers 类:

Public Class Customer
    Implements IComparable
    Public Name As String
    Public CreditLimit As Decimal

    Public Sub New(ByVal CustomerName As String, _
      ByVal CustCreditLimit As Decimal)
        Name = CustomerName
        CreditLimit = CustCreditLimit
    End Sub

    Public Function CompareTo(ByVal obj As Object) As Integer _
      Implements IComparable.CompareTo
        Dim c As Customer = CType(obj, Customer)
        If CreditLimit > c.CreditLimit Then Return 1
        If CreditLimit < c.CreditLimit Then Return -1
        Return 0
    End Function
End Class

实现的是一个类似的 Product 类,不同在于 CompareTo 函数比较的是产品价格,而不是客户的信贷限额:

Public Class Product
    Implements IComparable
    Public Name As String
    Public Price As Decimal

    Public Sub New(...)...
    Public Function CompareTo(...)...
End Class

现在,Comparer 类提供一般的比较操作:

Public Class Comparer(Of itemType As IComparable)
    Public Function GetLargest(ByVal Item1 As itemType, _
    ByVal Item2 As itemType) As itemType
        Dim i As Integer = Item1.CompareTo(Item2)
        If i > 0 Then Return Item1
        If i < 0 Then Return Item2
        Return Nothing
    End Function
End Class

可以用不同类型的对象实例化 Comparers

Dim pc As New Comparer(Of Product)
Dim prod1 As New Product("LittleOne", 10)
Dim prod2 As New Product("BigOne", 100)
Dim lp As Product = pc.GetLargest(prod1, prod2)
MessageBox.Show("The more expensive product is: " & lp.Name)

Dim cc As New Comparer(Of Customer)
Dim cust1 As New Customer("SmallCo", 1000)
Dim cust2 As New Customer("LargeCo", 5000)
Dim lc As Customer = cc.GetLargest(cust1, cust2)
MessageBox.Show("The customer with a higher limit is: " & lc.Name)

如果没有泛型,就需要为想要比较的每种类型的对象定义一个比较类(例如,ProductComparerOrderComparer)。

泛型可以显著减少在许多常见情形下花在编码上的精力。如果您有多个类,而它们只是操作的对象类型有区别,则可以考虑使用泛型。

Using 语句

Using 语句是用于获取一个对象、利用它执行代码并立即释放它的快捷方式。大量框架对象(例如,图形、文件句柄、通信端口和 SQL 连接)都需要您释放所创建的对象,以免在应用程序中造成内存泄漏。假设您使用 brush 对象来画矩形:

Using g As Graphics = Me.CreateGraphics()
    Using br As System.Drawing.SolidBrush = _
        New SolidBrush(System.Drawing.Color.Blue)
        g.FillRectangle(br, New Rectangle(30, 50, 230, 200))
    End Using
End Using

一旦图形和 brush 对象使用完毕,您就想要处置它们,Using 语句使得处置工作非常容易。在 Visual Basic .NET 中,与使用 Try / Catch 并释放 Finally 块中的对象相比,Using 语句更为简洁。

Continue 语句

Continue 语句跳过一个循环的下一次迭代,使得循环逻辑更加简练和易于阅读:

Dim j As Integer
Dim prod As Integer
For i As Integer = 0 To 100
    j = 0
    While j < i
        prod = i * j
        If prod > 5000 Then
            'skips to the next For value
            Continue For
        End If
        j += 1
    End While
Next

Continue 语句非常简洁,并使得跳出内部循环十分容易,无需借助标签和 goto 语句。Continue 语句用在 For、While 和 Do 循环中。

Global 关键字

Global 关键字使得命名空间层次结构顶部的根(或空)命名空间可以访问。以前,您不能在公司的命名空间层次结构中定义 System.IO 命名空间:

Namespace MyCompany.System.IO

这样做会搞乱对应用程序中框架的 System 命名空间的所有引用。现在,您可以使用 Global 关键字来消除命名空间的歧义:

Dim myFile As Global.System.IO.File

在想要确保生成的命名空间引用指向预期位置的代码生成方案中,Global 关键字特别有用。我想您将会注意到,Visual Basic 本身在所有生成的代码中使用 Global 关键字。

IsNot 运算符

IsNot 运算符是 Is 运算符的一个副本。与在引用一个对象之前要检查它是否已经实例化的频繁和笨拙不同:

If Not myObj Is Nothing Then

IsNot 可以让您使用直接比较,不必用到 Not 运算符:

If myObj IsNot Nothing Then

同样,在检查两个对象实例是否有区别时,也可以不用到 Not 运算符:

If MyObj1 IsNot MyObj2 Then

虽然这只是一个小小的更改,但 IsNot 却填补了 Visual Basic 语言在一致性方面的缺陷,从而使您的代码更加整洁。

TryCast 语句

在 Visual Basic 2003 中,可以采用以下两种方式之一将一种类型的对象强制转换为另一种类型:

Dim p As Product 
p = CType(obj, Product)
p = DirectCast(obj, Product)

这里,从一种类型转换为另一种类型用的是 CType,而 DirectCast 要求对象之间有继承或实现关系。其问题是,如果 obj 不能转换或强制转换为 Product 类型,则会出现运行时异常。

进入新的 TryCast 语句。它的用法与 CTypeDirectCast 相同,不同在于如果不能进行强制转换,则返回结果为 Nothing:

p = TryCast(obj, Product)
If p IsNot Nothing Then
    'use the p object...
End If

运算符重载和转换运算符

Visual Basic 2005 最为强大的一个新增功能是运算符重载。运算符重载可以让您定义可对任何想要的类型起作用的运算符,甚至是对创建自己的基类型的指针进行运算。

运算符重载的典型例子是对复数进行加运算。+ 运算符重载的一个简化的 Complex 类如下所示:

Public Class Complex
    Public Real As Double
    Public Imag As Double

    Public Sub New(ByVal realPart As Double, ByVal imagPart As Double)
        Real = realPart
        Imag = imagPart
    End Sub

    Shared Operator +(ByVal lhs As Complex, ByVal rhs As Complex) _
        As Complex
        Return New Complex(lhs.Real + rhs.Real, lhs.Imag + rhs.Imag)
    End Operator
End Class

使用 + 运算符以一种很直观的方式对复数进行加运算:

Dim lhs As Complex = New Complex(2.0, 2.5)
Dim rhs As Complex = New Complex(3.0, 3.5)
Dim res As Complex = lhs + rhs
'res.real = 5.0, res.imag = 6.0

您也可以为您的自定义类型重载转换运算符。如果您试图将 res 的值转换为 CType(res,String),就会产生编译器错误“Complex cannot be converted to String”。如果您试图查看 Res.ToString 的值,就会得到 Complex 值,因为 ToString 默认显示该类型名称。可以通过重载 CType 转换运算符和 ToString 方法来解决这些问题,如下所示:

Public Shared Narrowing Operator CType(ByVal Value As Complex) _
    As String
    Return Value.Real.ToString & "i" & Value.Imag.ToString
End Operator

Public Overrides Function ToString(ByVal Value As Complex) As String
    Return Value.Real.ToString & "i" & Value.Imag.ToString
End Function

现在,CType(res,String) 和 res.ToString 的返回值就是您预期的值:“5.0i6.0”。转换运算符必须以 Narrowing 或 Widening 来声明,从而指明转换方式。

属性访问器可访问性

Visual Basic .NET 属性时常困扰我的一个问题就是,GetSet 访问器必须具有相同的可访问性(Public、Friend 或 Private)。如果您想创建一个只读的公共属性(只有 Get 是公开的),就不能在组件中使用 Set 访问器来进行强制检验或自定义属性处理。

由于在 Visual Basic 2005 中,SetGet 的限制条件更多,所以 GetSet 访问器具有不同的可访问性设置:

Private _myProp As String
Public Property MyProp() As String
    Get
        Return _myProp
    End Get
    Friend Set(ByVal value As String)
        If value.Trim.Length > 0 Then
            _myProp = value.Trim
        Else
            value = "<no value>"
        End If
    End Set
End Property

这在团队开发环境中特别有用,也有助于个别开发人员最大程度地重用他们的代码。

自定义事件访问器

事件访问器可以让您定义自定义事件,控制当客户端添加和删除处理程序并引发您的事件时执行什么操作。假设您有一个自定义类,您在其中引发了事件 RateChanged。采用以下两种方式之一声明标准事件:

Public Event RateChanged()
  'or
Public Event HoursChanged As EventHandler

以这种方式声明的事件有一个自动托管的后备存储。换句话说,由系统处理事件的托管和分派方式。一般这样就行,但有时您需要对通知事件侦听器的方式进行更多的控制。

请使用新的 Custom 关键字声明自定义事件及其访问器。当您在事件声明中按下回车键时,Visual Basic 2005 会以生成 Property 访问器的同样方式为您创建代码原型:

Public Custom Event NameChanged As EventHandler
    AddHandler(ByVal value As EventHandler)
        'hook handler to backing store
    End AddHandler

    RemoveHandler(ByVal value As EventHandler)
        'remove handler from backing store
    End RemoveHandler

    RaiseEvent(ByVal sender As Object, ByVal e As EventArgs)
        'invoke listeners
    End RaiseEvent
End Event

当客户端为您的事件添加或删除一个处理程序时,AddHandlerRemoveHandler 例程就会运行。当引发事件时,RaiseEvent 例程就会执行。通过这种方式,您就可以根据管理该事件的后备存储的方式来采取特定的操作。当您以这种方式创建自定义事件时,就可以认为该事件就是一种属性。

这种关键字起到作用的一种情况就是当您的对象可以序列化,同时有一个事件是由不可序列化的委托对象处理的时候。当您试图以常规事件序列化对象时,序列化会失败,因为备份该事件的存储是不可序列化的。Rocky Lhotka(另一个 Visual Basic MVP)对它的工作方式有一个解释,并且在他的网络日记专区(位于 http://www.lhotka.net/WeBlog/CommentView.aspx?guid=776f44e8-aaec-4845-b649-e0d840e6de2c)中给出了详细的示例。

部分类型

局部类型允许定义跨多个源文件的单一类型。这种类型定义在其中一个文件中有一个标准声明:

Public Class TestPartial
    'implementation...
End Class

在其他源文件中,您可以使用 Partial 关键字来通知编译器该代码是原始类定义的延续:

Partial Public Class TestPartial
    'implementation...
End Class

在 IDE 中,由设计器生成的类中的代码由 Partial 关键字加以标记,并与用户代码分开。在项目中添加一个名为 TestForm 的窗体,并查看产生的源文件。您也许会惊讶地看到,除了窗体的类声明外,就什么都没有了。以前用于进入 Windows Form Designer Generated Code 区域的所有由设计器生成的代码在哪里呢?

现在,由设计器生成的代码放在 TestForm.designer.vb 文件中,可以通过单击解决方案资源管理器工具栏中的 Show All Files 按钮来查看。您不应该更改这个文件中的代码,但如果您想在以后重新生成设计器文件时保留某些东西,则可以将所需的内容复制并粘贴到 TestForm.vb 文件中。

项目团队可以通过给每个开发人员一个包含该类的部分的文件来利用部分类。既然可以将一个类定义分别放在多个源文件中,就不需要多个开发人员共享访问相同的源文件了。

应用程序级事件

另一个您会想利用的新功能就是一组在名为 MyApplication 的部分类中可用的新的应用程序级事件。请在解决方案资源管理器的 My Project 项中找出一个名为 MyEvents.vb 的文件。您也可以在应用程序设计器中的 Application 选项卡的 View Code 按钮后面找到这个文件。

这些新的应用程序事件与 ASP.NET 应用程序的 global.asax 文件中的应用程序事件很相似。公开了五个事件:

Shutdown

Startup

StartupNextInstance

NetworkAvailabiltyChanged

UnhandledException

前三个事件是在应用程序启动和关闭时激发的。当计算机的网络状态改变时,激发 NetworkAvailabilityChanged。请在 UnhandledException 事件中加入代码,以防引发一个您在其他地方没有处理的异常。

无符号类型

Visual Basic 2005 全面支持无符号类型。无符号类型名称为:

SByte

UShort

UInteger

ULong

无符号类型与正常类型的工作方式一样,但无符号类型只能存储正整数。

在 Visual Basic 中,无符号类型最常用于调用参数为无符号类型的 Win32 API。以下代码展示了一个 Windows MessageBox API 调用:

Public Enum WindowsMessageResult As UInteger
    OK = 1
    Cancel = 8
End Enum

Private Const MB_OK As UInteger = 0
Private Const MB_ICONEXCLAMATION As UInteger = &H30

Private Const MB_OPTIONS As UInteger = MB_OK Or MB_ICONEXCLAMATION

Private Declare Auto Function Win_MB Lib _
    "user32.dll" Alias "MessageBox" _
    (ByVal hWnd As Integer, ByVal lpText As String, _
    ByVal lpCaption As String, ByVal uType As UInteger) _
    As UInteger

Public Function MessageThroughWindows(ByVal message As String, _
    ByVal caption As String) As WindowsMessageResult
    Dim r As UInteger = Win_MB(0, message, caption, MB_OPTIONS)
    Return CType(r, WindowsMessageResult)
End Function

请注意,Win_MB 声明中的最后一个参数和返回类型都是 UInteger 类型。

如果您需要存储比 Integer 大的值,无符号类型也可以帮助您节省内存空间。Integer 类型使用 4 个字节的内存空间,可以存储的正负值最大为 2,147,483,648。UInteger 类型也使用 4 个字节,但可以存储 0 到 4,294,967,295 之间的值。如果没有无符号类型,您就需要使用占据 8 字节内存空间的 Long 来存储这么大的值。

默认实例

对 Visual Basic .NET 的另一个改变就是缺少窗体默认实例,这导致许多开发人员从 Visual Basic 6.0 移植时遇到了困难。在使用窗体之前需要创建窗体的一个实例:

Dim frm As New Form2
frm.Show()

Visual Basic 2005 支持窗体默认实例,因此您可以使用熟悉的语法:

Form2.Show()

最好是通过 My.Forms 集合访问这个默认窗体实例:

My.Forms.Form2.Show()

编译器警告

Visual Basic 2005 支持编译器警告,它使您对可能在运行时产生问题的地方有所了解。警告显示为代码底下的绿色波纹线(错误显示为蓝色波纹线)。

编译器警告包括递归属性访问、重叠的 catch 块或 case 语句、创建没有返回值的函数等等。我最喜欢的警告是警告对未初始化的对象进行变量引用:

Dim cust As Customer
If cust.Name.Length = 0 Then
  '...
End If

这里背o编译器在 If 语句中的 cust 底下加了一条绿色波纹线。显示的警告语是“variable 'cust' is used before it has been assigned a value”。在运行时会导致空引用异常。我不知道有多少次在运行时发现代码的此类错误,现在,编译器可以在编译时就发现这些错误。

顺便提一句,当您在编辑器中键入如下代码时,编译器开始的时候会在 Dim 语句的 cust 底下加上一条绿色波纹线,并给出警告语句“unused local variable 'cust'”。刚开始的时候您会觉得这有点莫名其妙,因为这个变量才刚刚添加,但最后您会发现它有助于使代码更加整洁。

在 IDE 中,并不是在 Task List 中显示所有的错误,而是有一个新的 Errors List 窗口,它将消息分成错误、警告和消息(请参见图 2 和图 6)。在应用程序设计器的 Compile 选项卡中,您可以控制让编译器对警告或者错误做出标记,它有一些复选框,用于禁用所有警告或者将所有警告视为错误。

使用 Option Strict On 语句来生成几种情况下的编译时错误。如果您试图进行隐式收缩转换(这样会造成数据丢失),则 Option Strict 会对您的代码做出标记:

Dim iValue As Integer
Dim lValue As Long
iValue = lValue     'narrowing conversion error
lValue = iValue     'ok

如果您使用晚期绑定,也会产生错误。一个对象当被指定为 Object 类型的变量时就是晚期绑定:

Dim myObj As Object
Call myObj.method1()    'late binding error

现在认为使用 Option Strict 是进行 Visual Studio 编程的最佳做法。只要可能,就应该在代码中打开 Option Strict。也可以在 Tools | Options 菜单中打开 Option Strict,方法是选中 Project 设置下 Option Strict 对应的复选框。另外还可以在单个类或模块文件的顶部添加 Option Strict On 语句。

显式数组界限

现在可以使用显式数组界限来声明数组,以使得声明更加简练:

Dim a(10) As Integer        'old way
Dim b(0 To 10) As Integer   'new way

Visual Basic 中数组界限仍然是从 0 开始的,因此如果数组下限不为 0 就会产生编译器错误。

小结

Visual Basic 2005 语言增加了几个主要功能以及许多较小的增强功能,从而在使用上更加方便,并大大提高了开发人员的工作效率。该语言已变得更加完善,并且在一些主要功能上(例如,XML 注释和运算符重载)与 C# 更为相似。

再加上大量的 IDE 增强,可以认为 Visual Basic 2005 是已发布的 Visual Basic 版本中的最好版本。不只是在语言功能上有所改进,同时也十分易于使用。唯一的遗憾是,现在还不能使用 Visual Basic 2005 构建生产应用程序。我想当您试用过该 Beta 版之后,也会同意我的观点的