Visual Basic.NET - 一个全新的开发模式和编程利器

NET_vs_J2EE 2001-09-06 07:00:23
Ted Pattison

本文假设你具有的Visual Basic基本知识。


摘要:

VisualBasic.NET是Visual Basic为Micosoft.NET构架而作重大改进的成果。许多改进使得Visual Basic.NET更强大,更易用,并赋予其以前需要C++这样的语言才具有的控制系统资源的能力。对象的继承功能是一个重要的改进,Visual Basic.NET中,所有的受管(Managed) 类型都继承于System.Object对象。

另外一个非常重要的新的特性是垃圾回收特性(Garbage Collection),这是由通用语言运行库(Common Language Runtime, CLR)提供的,它改进了内存管理。系统通用类型允许各种对象的交互,且为Visual Basic.NET提供了更强大的性能和灵活性。

如果你还没有用过Visual Basic.NET, 现在正是熟悉这一语言的时候。本文的任务就为Visual Basic.NET和Microsoft.NET平台做一个简单和广泛的介绍。为了了解Visual Basic.NET的各个方面,你必须对Microsoft.NET平台的一些核心的概念有一个了解。为了使读者能够对Visual Basic.NET有一个概念性的理解,我将从新的编程模型和平台执行引擎的上层架构-通用语言运行库(CLR) 开始介绍。

在介绍通用语言运行库(CLR)的概念以及它的工作机理时,我将会 给出Visual Basic.NET的一些例子。你将看到为了适应CLR以及相应的编程模型,Visual Basic®经历了重大飞跃。相应的,Visual Basic.NET增加了许多面向对象的特性,并且与以前的版本相比较,类型安全的层次更高了。

需要引起重视的是Visual basic.NET略去了一些以前版本的语法,这意味着Visual Basic 6.0的代码在未经过必要的修改之前是不能在Visual Basic.NET上编译的。而且,编写优秀的Visual Basic.NET代码将会涉及到Visual Basic 6.0不支持的新的特性和语法。所以,从Visual Basic 6.0向Visual Basic.NET迁移通常需要改写代码, 而不是简单照搬。

由于对老的VBA运行库或ActiveX数据对象(ADO)的依赖,从Visual Basic 6.0向Visual Basic.NET迁移可能需要重写大量代码。为了成为优秀的Visual Basic.NET程序员,你必须尽量利用建立在CLR上的共享类型库。

Visual Baisc.NET是少数几个专门为CLR和.NET平台而设计的语言。另一个广为关注的是C#。像许多程序员一样,你很可能对Visual Basic.NET和C#的区别很感兴趣。然而,C#是专门为熟悉的C和C++人设计的。在本文中,我将提出这两种语言中的关键差别,以便你做出更喜爱那种语言的判断。但是,我坚信,不论那种语言都能完全利用CLR和.NET平台的优势。现在,让我开始介绍.NET平台的几个关键概念。

CLR的角色

为.NET平台而编写的代码是在CLR控制下运行的。需要重视的是CLR是用来代替现有的COM运行层、Microsoft Transaction Service (MTS) 和COM+的(见图1)。 就像你看到的,CLR代替了Visual Basic运行库的层次。

显而易见的事,CLR不可能在一夜之间代替COM. 许多公司在为编写COM,MTS和COM+的程序上有很大投资。所以,为CLR编写的程序和基于COM程序之间的交互式很重要的问题。Microsoft为这个问题投入了大量的力量来保证CLR与COM之间的交互层能尽可能的顺利和高效的工作。

专门在CLR的控制下编写的代码被称为受管代码(Managed Code)。早期依靠COM和WIN32API的代码被称作非受馆的代码。Visual Basic6.0只能编写非受管代码,而Visual Baisc.NET则正好相反。这是这两个版本之间最基本的区别。

Visual Basic小组为新版的Visual Basic提供了能产生受管的代码的新的编译器(VBC.EXE)。例如,你可以将Visual Basic的源代码让Visual Basic.NET的编译器编译来产生受管的DLL文件。请注意,不像以前版本的Visual Basic, Visual Basic.NET的源代码使以.VB为扩展名的。虽然Visual Studio.NET能使编写Visual Baisc.NET的程序变得更容易,但这不是必需的。你可以在任何编辑器中编写Visual Baisc.NET的程序,然后用命令行来编译出EXE和DLL程序。

Visual Basic.NET的源代码管理变得很容易。这是因为一个工程中的所有代码可以保存在一个源代码文件中。不像以前版本的Visual Basic, 你不必为每一个类编写一个单独的.CLS文件。当然,仍然可以使用不同的.VB文件来维护源代码,然后编译成一个二进制文件供发行。

另一个很好的特性是Visual Basic.NET可以自动使用NMAKE.EXE和MAKEFILE工具来联接可执行文件。一些拥有许多分开的源代码文件但需要不断的编译链接来测试的公司将会发现这是一个较Visual Basic6.0的重大改进。

受管类型

让我们来用Visual Basic.NET编写和编译一个简单的控制台应用程序。在看图2的代码时,因当注意的是,CLR的代码使用的是受管类型。这个例子包含两个受管类型:MyApp和Class1。

MyApp组件只包含一个函数Main,这个函数是控制台程序的入口。Main函数的实现代码中构造了Class1的一个实例并调用Method1函数。Method1函数的返回值用来在控制台窗口中输出信息。这个例子显示了Visual Basic.NET带来的语法上的易用性。现在,你可以在一行代码中申明和初始化变量。

另一个受管类型的例子是图2中的Class1.这个类只包含一个方法Method1。Method1显示了Visual Basic.NET易用性的另一个方面。她使用Return申明来返回值给调用者。使用Visual Basic.NET不再需要用函数名来指定返回值。

最后,看一下图2中从CLR类库访问Console类的语法。注意到调用Console类WriteLine方法使用System修饰。在这个例子中,System作为名字空间使用。名字空间对CLR很重要,对Visual Basic.NET也一样。在Visual Basic中当你在其他类库中解析受管类名时,你必须理解名字空间。

名字空间是用户指定的在其中定义受管类型的区域。大多数内建的CLR类型是在System名字空间中定义的,例如System.Object,System,Int32和System.String. 注意一个名字空间可以嵌套在另一个中,一个例子是System.Data,在其中定义了System.Data.DataSet.

Visual Basic.NET提供了在一个名义空间中使用Import申明来简化语法的方法。例如,假设你在Visual Basic.NET源代码文件中加入:

Imports System

这个Import申明使你能不使用全称来调用WriteLine方法。就像这样:

Console.WriteLine ‘ this can be used
System.Console.WriteLine ‘ instead of this

注意到在C#中using申明有和Visual Basic.NET中Import同样的作用。请注意,Import申明除了使你的代码在调用同一名字空间的其他受管类型更简洁之外什么也没有做。

使用Visual Basic.NET编译器

你可以用下面的命令行将图2的源代码编译成控制台应用程序。

vbc.exe /target:exe hello.vb

虽然这个例子中的代码十分简单,但这可以使我阐明开发.NET平台软件的关键的问题。当你成功的使用Visual Basic.NET建立了一个工程时,你创建了含有一个或多个受管类型的二进制文件。这些受管类型能在CLR的控制下被调用和运行。

CLR的编程模型能识别四种主要的受管类型:类,接口,结构和枚举类型。图3显示了每种类型在Visual Basic.NET中的形式。

不像以前版本的Visual Basic, Visual Basic.NET不支持用户定义的类型(UDTs) 和Type关键字。UDTs被结构类型所取代。结构类型和UDTs相似之处在于两者都是有关数据的类型。它可以被分配到堆栈中也可以置于另一个类型中。结构在数据方面可以作为类的一个替代品,因为结构在储存或传递数据时更高效。需要指出的是结构类型比UDTs更为强大,结构可以访问公共方法甚至可以实现接口。你应该把结构看作一种创建轻量对象的受管类型。

不论CLR还是Visual Baisc.NET都提供了对基于接口编程的完美支持。不像Visual Basic 6.0, 你不再需要使用类的结构来定义接口。图3显示了接口定义和在类中实现的语法。从这个简单的例子中,你可以看到基于接口的编程语法远比Visual Basic6.0中的语法优美。

共享成员是CLR编程中另一个关键的概念。对Visual Basic程序员来说,这可能是一个新的东西。例如,类中除了实例的方法和字段外,还可以包含共享方法和字段。这与Visual Basic6.0有很大不同。在VB6.0中,类只包含实例成员。

共享成员于实例成员的地不同之处在于共享成员可以不需建立实例而访问。让我们看图3中的简单的例子。考察图3的Class1,Class1被标为共享方法。注意,Shared关键字与C#,C++,Java语言中的static关键字有相同的意义。

客户可以通过简单的使用共享方法名及类名来调用共享函数。就像这样:

Class1.Method3

另一个有意思的东西是CLR的编程模型和Visual Basic.NET的模块类型没有直接的对应关系。Visual Basic.NET加入模块类型主要是为了提供一个和以前版本的Visual Basic模块相对应的东西。然而,当你建立DLL或EXE时,Visual Basic.NET编译器将你源代码中的模块类型转换为可以被CLR调用和运行的受管类型。

你可以把模块看作是不能创建对象特殊的类。他只能包含共享成员;不能包含实例。你必须小心,虽然模块的成员是缺省共享的,如果你加上Shared关键字,你将得到一个编译错误。

最后,你应当注意,在Visual Basic.NET中模块类型比类在语法上可以更简单。你可以不指定模块的名字而调用共享方法。当你调用类的共享方法时,你必须指定类的名字,或加入类的Import申明。

CLR编程模型同样加入了另外一些熟知的抽象。类和结构使用子段来定义存储单元,使用方法提供类的行为。CLR也使用属性。就像在先前版本的Visaul Basic中那样,属性是一种方法,(或一组方法)。对客户来说,属性是公开的方法。虽然定义方法的语法在Visual Basic与Visual Basic.NET中不同,但使用属性的初衷是一样的。重要的一点是属性这个抽象同样被CLR编程模型认可。

你必须注意,CLR,向Visual Basic 6.0重的COM一样,也支持索引属性。你会不时的发现这样的代码:

Dim s As String
s = Object1.Property1(10)
索引属性也可以被赋予类的缺省属性。(C#使用 “Indexer”术语来指定为缺省的索引属性)。这是一个访问缺省索引属性的代码例子。
Dim s As String
s = Object1(10)

注意,除非属性是被索引,否则不能作为缺省属性。这是与早期版本的Visual Basic有很大不同的地方。下面是一个从文本框取得非索引缺省属性的Visual Basic代码。

Dim var1 As Variant
var1 = frmMain.txtCustomer

Visual Basic的早期版本由于包含非索引的缺省属性,造成很严重的模糊性。编译器如何知道你是要给文本框的缺省属性赋值还是要引用文本框对象?

在Visual Basic中解决这种模糊性的经典方法是使用Set关键字来区分对对象的引用还是对对象缺省属性的访问。例如,如果你要引用文本框而不是访问缺省对象,你使用下面的代码:
Dim var1 As Variant
Set var1 = frmMain.txtCustomer

就像我们刚看到的那样,早期版本的Visaul Basic由于非索引缺省属性的原因,需要用Set关键字。由于Visual Basic舍弃了非索引缺省属性,Set关键字不再有用。这意味着如果你引用对象是使用Set关键字,你会得到编译错误。必须承认,这是从Visual Basic6.0移向Visual Basic.NET是一个较大的语法改变。

代理(Delegates)和事件

代理是CLR编程模型中处于中心地位的新的概念。代理是一种特殊类型的受管类(Managed Class),它能让你使用类型安全的指针。每一个代理类型都基于一个单一的方法签名。但你创建代理对象实例时,你必须提供该方法实现的地址和与之相应的签名。当你创建了代理实例时,你很容易调用方法。

图4显示了Visual Basic.NET中声明和使用代理的最基本的语法,尤其应注意Delegate和AddressOf关键字的使用。你也能从例子中看到一种普通语法和一种简化语法。两种语法的结果是一样的。一旦你理解了代理的机制,你就会发现CLR是如何提供诸如多重代理和事件这些高级特性的支持的。
多重代理就是一组函数指针的集合,它能为用同一行代码执行不同的一组函数提供方便。当你使用Delegate关键字,Visual Basic.NET编译器产生一个多重代理而不是单一代理。这使你能用一个代理来调用多个方法。

下面的代码是上一个代理例子的变化。

Dim d1, d2, d3 As Delegate1
d1 = AddressOf Sub1
d2 = AddressOf Sub2
‘ create d3 which is a multicast of d1 and d2
d3 = CType(System.Delegate.Combine(d1, d2), Delegate1)
d3(“Firing two method implementations at once”)

第三个代理,d3,是另两个代理的组合。最后一行代码执行Sub1和Sub2方法。虽然这个例子使用多重代理执行两个方法,你可以联系和执行任意多个方法。CLR会处理大量的细节来执行这些调用。你时要保证代理和所有的方法共享一个签名。

既然你已经理解了多重代理的基本概念,你可以开始了解CLR是如何支持受管类的事件的。CLR的事件构架是基于这将一个概念:源对象使用多重代理来执行一个或几个执行目标对象的方法。

就像在Visual Basic6.0中一样,Visual Basic.NET的类除了方法,字段和属性外还能包含事件。图5显示了注册一个源类和两个对象类所需的基本代码。在Visual Basic.NET中,事件的工作方式与Visual Basic6.0中的一样,语法也是如此。例如,Visual Basic.NET提供熟悉的关键字,如Event, RaiseEvent和WithEvent。Visual Basic.NET引入Handle关键字来创建目标方法。

虽然编写事件的语法没有大的改变,但实现的机制与Visual Basic6.0有很大的不同。在以前版本的Visual Basic中,事件是基于COM和ConnectionPoint接口的。就像我提到过的那样,CLR中的事件是基于多重代理的。包含事件的类可以用来构造源对象,它可以向目标对象发送通知。

Event, RaiseEvent, WithEvent和Handle关键字的使用指导Visual Basic.NET编译器省略许多幕后注册代理的工作。这意味着你不必要使用代理就能直接发出和接听事件。注意,许多额外的效率是Visual Basic独有的,在其他受管语言如C#中是没有的。

我刚才简单介绍了CLR编程模型中的不同受管类型。现在,我已讲述了基本的知识,接下去将更深入的探讨如何编译受管的可执行代码。

Microsoft中间语言和即时编译

就像提到过的那样,CLR提供.NET平台前所未有的各种语言的集成。这意味着Visual Basic编译器,和其他所有的受管语言的编译器,如C#一样,必须遵守同一些规则。其中一个重要的规则是可执行的指令必须被编译成符合Microsoft Intermediate Language (MSIL)形式的DLLs或EXEs.

MSIL是一种和传统的汇编语言及相似又有不同的代码。和汇编语言相通的是,它包含低级的涉及寄存器输入输出的指令。然而,完全不同的是,它与任何操作系统和硬件平台无关。这意味着当包含MSIL的EXE或DLL被部署到部标计算机时,它必须经历一个最后的即时编译过程来转换为特定机器的汇编代码。

使用MSIL的第一个好处是当JIT编译时,能确认受管代码是类型安全的。CLR依靠这个过程来保证在DLL或EXE中不滥用指针,或使用非法的指针转换。这使得CLR不受通常的系统攻击的影响。从不受信任的站点下载的受管代码的计算机可以在及时编译是保护自己而为受管代码则不能。

MSIL的第二个显著的好处使是你的DLL和EXE和任何操作系统和硬件平台无关。

Microsoft现在已准备在Windows2000,WindowsNT,Windows98,Windows95上发行CLR.
然而,CLR的强大能力来源于它使你的DLLs和EXEs能在不时Intel x86的平台上运行。

在不久的将来,你能看到在WindodsCE下运行的CLR。同样,你完全可能在其他操作系统和硬件平台上看到CLR. Visual Basic代码能在手持PocketPC上运行的梦想已变成了现实。

CLR作为COM更好的替代品

当你在担忧Microsoft可能在长期计划中用CLR代替COM时,你应该理解将旧的运行环境用新的来代替所能带来的优势。CLR和.NET平台的设计构架能集中COM的优势,与此同时,减轻编写和部署基于COM的应用程序的困难。特别的,CLR剔除了许多COM的令人沮丧的问题。如语言的互相访问,程序部署,组件版本。就像你可能猜到的那样,CLR致力于消除编写和理解基于COM的分布式运用程序的许多使人困惑的细节。

COM的历史饱受诸如不同语言的相互访问的问题的困扰。虽然在非受管的语言中存在一些相互访问的可能性,但这远不能满足需要。例如,C++程序员很容易编写出不能被Visual Basic或脚本语言重用的DLL组件。许多内建的C++类型如字符串,数组,指针,很难或几乎不可能被其他语言访问。

CLR保证更高水平的相互访问能力。CLR编程模型是基于通用类型系统(见图6)。每一种受管语言都被置于这张图的顶端并映射内建类型的核心。


Figure 6 Universal Type System

就如你在图6中看到的那样,CLR类型系统定义的一组可预测的基本类型。其中包括像整数,浮点数。CLR同样定义了一些标准的类像字符串,数组和异常。

像Visual Basic.NET,C#这样的语言提供了一些关键字来直接映射许多内建的CLR类型。例如,Visual Basic.NET提供Interger关键字,就像在C#中的int一样。两种类都直接映射到CLR的System.Int32类型。像你看到的,CLR比COM改进的地方是统一了一组通用的标准类型,这些标准类型在各种受管语言中是共享的。

你应该知道CLR提供一些并不是所有受管语言支持的类型和特性。例如,CLR类型系统提供各种不同的无符号整数的内建类型。无符号整数在C#中是支持的,但在Visual Basic.NET中不支持。这意味着C#的程序员能可以创建其他语言很难或不可能访问的使用无符号整数的对象。

为了避免程序员错误的创建其他语言不能访问的组件,Microsoft编写了一个称为通用语言标准的文档(CLS)。CLS文档定义了CLR类型和特性的一个子集,组件和语言必须支持这个子集以保证受管语言之间的交互能力。

Visual Basic.NET完全符合CLS. 另外,内建在CLR中的类库能为符合CLS标准的语言完全访问。这对Visual Basic程序员来说是一个好消息。在过去,由于他们选择的语言,有些内部的平台(如Win32API和OLE32.DLL)是不能访问的。能完全访问CLR使得Visual Basic.NET在和其他受管语言比较时,能完成同样的任务。

从图6中可以发现,CLR的类型系统在很大程度上依赖于继承。整个类型系统建立在单继承的构架上。所有的受管类型都来源于Sysem.Object单一的根类。当你建立一个不是显式的从其它类继承的类,这个类就从System.Object继承。这意味着这种类的申明:

Public Class Class1
‘ class member declarations go here
End Class
和下面类的申明是等价的。
Public Class Class1
Inherits System.Object
‘ class member declarations go here
End Class
如果你要从用户定义的类出发,定义新的类,语法是这样的:
Public Class Class2
Inherits Class1
‘ class member declarations go here
End Class

注意,Visual Basic.NET要求你用回车将被继承的类和Inherit关键字分开。如果你想要你的代码能迷惑那些什么都知道的C++程序员,你可以用分号来代替回车。

Public Class Class2 : Inherits Class1
‘ class member declarations go here
End Class

这种语法与C#和C++更相似。分号在继承时是必需的。我已十分迷恋这种语法因为我发现它更具有可读性,更可管理。哦,是的,我使用它十分渴望成为C++程序员。

一个关键之处是任何能创建实例的受管类实例都继承自System.Object.这也包括原始的如interger,long,double这类类型。这意味这任何变量不管是数据变量或引用都可以类型转换到System.Object类型。你应该知道,Visual Basic.NET已经把非受管的变量,如variant, iUnknown, iDispatch的功能移入System.Object中。

类型更丰富的组件元数据(Component Metadata)

.NET平台使用”module”术语来指受管的二进制代码如DLL或EXE。每一种受管语言必须有一种编译器能够建立各种类型的组件元数据(Metadata)来描述模块中所含的类型。从图7可以看到,模块保存了组件元数据和所含受管类型的MSIL代码。


Figure 7 A .NET Module

模块中的组件元数据与COM DLL中的存储类型信息的类型库相似,因为它为客户端程序提供公共的类型信息。(像枚举类型,结构,接口,类)然而,一些重要的不同使受管组件的类型信息比COM的丰富得多。

首先,每个组件元数据都必须带有单一的,高度可信的格式来描述受管类的类型信息。这消除了COM开发者遇到的在类型库和接口定义语言(IDL)中类信息格式失去可信度的问题。更重要的是,.NET开发者比COM开发者有利的多,这是因为你不必学习一种专门的语言如IDL来描述类型信息。自定义的类型总能用受管语言如Visual Basic.NET或C# 来描述。

COM与CLR的另一个重要的不同是受管的组件包含远比描述类更多的元数据。在COM中,类的类型信息使用coclass在类型库中定义。COM组件的coclass仅仅局限于描述组件支持什么接口。COM有非常严格的规定来区分接口和方法的实现。Coclass中只提供有限的信息正是这种思想的体现。

与COM中分开接口与实现不同,Visual Basic总是为各种用途的类提供一个缺省的接口,从而为编程者提供方便。当Visual Basic的客户端程序引用一个类的名字的时候,Visual Basic编译器不动声色地将引用转换到类的缺省接口。所以Visual Basic隐藏了COM需要将接口和实现正式分开的需要。

CLR的构架较COM相比更接近Visual Basic的类。一个受管类的元数据可以给出它的公共方法来作为接口。这提供了灵活性。不像COM,你不必定义一个单独的接口来与类相区别。关键的是你不一定使用接口,因为有时公共方法是更容易接受的替代品。

虽然CLR构架并不要求一个单独的接口,但你不能将此理解为基于接口的编程在编写受管代码时是不重要的。定义十分清晰的接口在编写插入兼容的类和一个大的应用程序中的一个小系统时是十分重要的。更重要的是,CLR类库经常使用接口。任何一个中级或高级程序员应该十分熟练的定义,实现,使用接口。

虽然Com和CLR组件都需要公开类型信息,但CLR与COM不同的是CLR需要将内部类型信息透露给系统。CLR使用内部类型信息来创建和管理对象。这使得CLR能完成许多以前COM需要组件DLL和客户端程序才能完成的任务。让我们看几个例子。

COM类型库不包含任何表示对像在内存中如何存储的信息。这是COM DLL的任务来为组件分配和时方内存。COM DLL同样负责为对象创建于COM相适应的虚拟表。在CLR中,这个任务别移交给了内在的运行环境。

CLR负责为对象分配和释放内存。当客户端发出创建受管类对象的请求时,CLR在运行时检查类的内在类型信息,并做出分配。这使得CLR能在运行时能分配正确的内存给对象。

CLR同样使用内部类型信息来为客户程序绑定方法和对象。这意味着受管的COM二进制代码,不像COM代码,不需要在运行时访问COM类型的虚拟表。

正像你看到的,CLR在运行时比COM负责更多的工作。这使得CLR构架能省略COM的许多复杂和额外的东西,像类工厂和虚拟表的工作。

垃圾回收和对象的生命周期

现在我将涉及内存管理的课题。我将提出COM和CLR的重要的构架差异。这与对象的生命周期有关,并极大的影响你写代码的方式。

COM使用引用计数的方式的管理对象的生命周期。当你释放COM对象的最后一个引用时,对象同时移出内存。当对象在Class_Terminate的实现代码中含有自定义的清除程序时,你得到的保证是这些代码将已事先决定的方式运行。在运行CLR的受管对象时不是这样的。

CLR使用垃圾回收技术来管理对象的生命周期。这与COM的引用记数的方式完全不同。CLR总是在垃圾回收堆中创建对象。当客户端程序释放对对象的最后一个引用时,对象并不立即被移出内存。取而代之的是,垃圾收集器在将来的某个不确定的时候将对象移出内存。

你会发现垃圾回收的方法比引用记数的方法更出色。主要有两个原因:一个是性能更优异;另一个是系统将有能力检测并阻止对象的循环引用。设计者觉得,这两个原因足已使我们放弃原来的COM方式,改之以垃圾回收的方式来管理对象生命周期。

偏好引用记数的主要原因是析构函数能及时和可预见的调用。由于CLR不使用引用记数,Visual Basic.NET不支持Class_Terminate函数。取而代之的是,受管类提供Finalize方法在对象移出内存时做清除工作。必须清楚的是这与COM的那种客户端一释放就调用析构函数的做法是不同的。

关于究竟那种方法更好—垃圾回收或是引用记数—的争论仍在激烈的进行。有些开发者将这种争论视为荣誉之战。虽然我对那种更出色不发表评论,但我可以肯定CLR使用垃圾回收技术,这将影响你的受管代码。

包和代码分发

像我先前提到的那样,模块是一个二进制单元,其中含有组件元数据和MSIL。然而,在受管代码的分发前还有一层抽象是包。它与模块相补充因为它与部署、版本很安全等问题相关。

有许多重要的细节与包和代码部署的问题有关。不幸的是,由于细节太多,无法在本文中全部涉及。我将只谈表面的问题,提供一个重要问题的总的介绍。

包可以被定义成组成部署单位的一个或多个模块。每个包都含有组件元数据的一个 列表,这个列表被称为表名单 (manifest).由于包的表名单 (manifest)含有类型可见性 ,组件版本和安全性这些重要数据,包这层抽象重要的。

每一个受管类型都必须存在于包的范围之内。在Visual Basic.NET你创建每个工程通常代表一个包。当你要使用另一个包的受管类型时,你的工程必须引用这个包。


当你为其他人编写包时,你必须界定哪些类型在包是外可见的。Public和Private关键字允许你公开或隐藏类或接口这些类型。注意,Visual Basic.NET允许你用Friend关键字来修饰公共类中的方法,使这个方法只能为包中的对象访问。

一种考虑包的方法是把它看作一个版本单位。你将类型定义为公共或私有的决定是很重要的。记住,只需要考虑公开给客户端程序的类型和类型成员的版本问题。包中的私有成员可以被移出或修改而不必考虑客户端程序。

当你用命令行程序VBC.EXE时,你必须为工程的被一个外部包带一个参数(/reference或 /r) 。例如,这是你的控制台应用程序使用外部包时调用Visual Basic.NET编译器的命令:
vbc.exe /target:exe /reference:MyLibrary.dll hello.vb

一个你不须显式指明地引用是对MSCORLIB.DLL的引用。由于这个包含有诸如System.Object等核心受管类,Visual Baisc.NET编译器自动加入对它的引用。其他受管类必须被显式的加入,否则你的代码不能被编译。当你的在工程中加入引用时,Visual Sudio.NET会自动传递适当的参数给Visual Basic.NET编译器。

在于多情况下,一个包只含有一个单独的DLL或EXE文件。缺省情况下,每个你用Viusal Basic.NET编译器编译的DLL或EXE既是模块又是包。然而,再在一些复杂的部署策略下,你可能希望一个包中含有多个DLL或许多资源文件。有一个Visual Basic.NET编译器开关允许你编译不是包的模块。这使得编译多模块的包然后用链接工具AL.EXE链接成为可能。

CLR能识别两种不同的包。第一种是私有的包,这种包被一个单独的应用程序使用。注意,私有的包只能部署在应用程序的目录或其子目录下。第二种是共享的包,能被多个应用程序使用。共享的包必须在使用前被安装到一个特殊的包缓存中。


Figure 8 Private Assembly with One DLL

图8和图9是包的全局图。图8显示了一个单一DLL中的私有包。图9显示了含有多个DLL共享的包的复杂情况。


Figure 9 Shared Assembly
DLL Hell的终结


当涉及程序部署和组件版本的问题时,CLR比COM更具优越性。由于在部署时需要注册ProgID,CLSID,IID和类型库等键值,COM赢得了脆弱和难以部署的坏名身。CLR部署的包和类型库不需要诸如注册表键值的东西。CLR在调用组件和运行时类型识别的问题上提供了更大的灵活性和适应性。


CLR同时在组件版本的问题上比COM有很大改进。这很大程度上取决于CLR支持同时部署,换句话说,CLR可以调用和启动同一个包的不同版本。CLR可以使两个不同的应用程序调用同一个DLL的不同版本,即使它们在同一个进程中。


同时部署是对COM的一个重大改进。在COM中,一个类(一个CLSID)只能在一个机器上部署一次。这意味着现在,创建新版本的DLL而不保持向下兼容性比以前更可以接受的。你可以部署不同版本的DLL,来同时适应新的和老的客户端程序。那种安装了新的DLL而使老的程序不能工作的时代一去不复返了。

CLR提供复杂的版本支持。然而,这种支持仅适用于部署代码在共享包中的情况。我将给出这种支持的简单介绍以使你能更好的了解为什么这比COM的版本支持要好得多。

当你编译一个引用共享包的客户端应用程序时,包的名字和版本号被记录在包的表名单 (manifest)中。不像COM,客户端程序总知道它是由哪个版本的DLL编译的。进一步的是,CLR能使开发者和管理员能够调节版本策略,使客户端程序决定调用那一个版本的共享包。客户端程序可以决定是调用它被编译时的那个版本的包或是调用最新的兼容包。

从这些简略的介绍中,你能发现CLR提供了一个部署程序和控制组件版本的更好的环境。你可能听到Microsoft的员工把这称为DLL Hell的终结。从我的观点来看,这是迁移到CLR和Visual Basic.NET的最诱人的地方。

从Visual Basic 6.0升级到Visual Basic.NET

向你看到的那样,在从Visual Basic 6.0和COM向Visual Basic.NET和CLR迁移的过程中,需要考虑许多问题和实现的细节。迁移本身在成本中占一定的比重。你也需要考虑改变整个开发队伍,还是迁移已存在的Visual Basic工程。

要清楚的是,CLR的编程模式提供了许多对于Visual Basic开发者来说新的面向对象的特性。本文远没有提到所有的新的编程特性和概念。对初学者来说,Visual Basic.NET提供了对结构化异常处理,共享类成员,参数化构造函数,方法重载和继承特性的支持。

不要低估Visual Basic 6.0程序员转变为Visual Basic.NET程序员所要化的时间。所有这些新的面向对象的特性都要花时间来掌握。为了正确的使用这些特性,我们有许多东西要学。

记住升级到Visual Basic.NET并不是简单的改变你写代码的方式。你应当尽可能多的使用CLR的内建类库。这些类库提供了诸如字符串处理,用户界面,数据库支持,XML处理和套接字编程等许多功能。

使用新的CLR库需要熟悉Visual Basic的程序员做出很大的改变。当你处理文本是,你可能想使用熟悉的函数,如UCase, InStr和VBA运行库中的StrComp函数。但你必须抵制这种诱惑转而使用CLR类库中的等价的函数。你可以想象,要改变程序员,从现有的VBA运行库,ADO, 和MSXML转向CLR库中的相应的函数是要付出一定的代价的。

一旦你跟上了Visual Basic.NET的新特性并开始从容的使用CLR类库,我想你会同意Visual Basic.NET提供了前所未有的良好的分布式应用程序平台。

然而,很难决定是否把现有的Visual Basic 6.0的程序移植到Visual Baisc.NET.将任何Visual Basic6.0的程序移植到Visual Basic.NET中都不是一件轻松的事。移植程序和组件库需要重新定义已有的类型,重写实现方法的代码。去掉对非受管库的引用并用受管的CLR库代替将增加移植花费。


有些公司将发现根本不值得将现有的Visual Basic6.0程序移向Visual Basic.NET.这样的话,你有两个选择。你可以用Visual Basic.NET从头重写工程,或原封不动的保存Visual Basic6.0工程。如果你想延用Visual Basic6.0的程序和组件库,你将很高兴的发现,CLR与COM之间的相互操作层是可靠和易用的。

CLR与COM的交互层使你能构建由受管和非受管代码混合的程序。这意味着你能混合使用Visual Basic6.0和Visual Basic.NET程序。我计划在下一期中进一步讨论这个问题。

结论

除非你深刻理解CLR和其相关的编程模型,你不可能完全理解Visual Basic.NET. 知道内在的类型系统和CLR的面向对象的特性是很重要的。一旦你有了这些基础,你就能掌握像Visual Basic.NET或C#那样的受管语言(Managed Language)。

在本文中,我描述了那些COM的实现方式被更好的更现代的CLR的实现方式所取代。然而,COM的思想在CLR和.NET平台下仍十分活跃。这主要涉及用组件来编写,重用,发布,和控制程序代码。如果有用的话, 你完全可以将CLR看成COM的最新版本。


--------------------------------------------------
Ted Pattison DevelopMentor的研究员和导师。他在DevelopMentor管理Visual Basic课程。它的书Programming Distributed Applications with COM and Microsoft Visual Basic 6.0第二版由Microsoft Press在2000年7月出版。
...全文
132 回复 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

16,721

社区成员

发帖
与我相关
我的任务
社区描述
VB技术相关讨论,主要为经典vb,即VB6.0
社区管理员
  • VB.NET
  • 水哥阿乐
  • 无·法
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧