type="Microsoft.VJScript.VJScriptCodeProvider,VJScriptCodeProvider,
Version=7.0.5000.0,Culture=neutral,PublicKeyToken=b03f5f7f11d50a3a"/>
上面这段代码表明任何人都可在.NETFramework中提供一种语言以供使用,只要该语言有一个编译器,并遵循几条基本的规则即可。
本章的后面将介绍一些由第三方提供的其他语言。
3.1.1VBScript何去何从
.NETFramework支持VisualBasic,不再支持VBScript。
VisualBasic不仅提供了VBScript原有的功能,而且比后者要强大许多。
不过尽管VBScript已经退出了舞台,但.NETFramework仍然支持其语法,只不过现在得到的将是完全的编译、数据类型以及新语言所带来的好处。
因此,即使您以前只使用VBScript,也不必担心——尽管需要作出一点调整,但在绝大多数的情况下,使用VisualBasic.NET还是很简单的。
3.1.2VisualStudio还是Notepad
大多数人假定,为了在ASP.NET页面中使用VisualBasic或C#,需要使用VisualStudio.NET,但事实并非如此。
CLR提供对各种语言的支持,各种编译器都是SDK的一部分。
因此要编写ASP.NET应用程序,只需要SDK(免费的)和自己喜欢的编辑器(如果您是一个Notepad的坚定用户,这可是个好消息)。
对编译而言,每种语言都有一个独立的编译器(这在本章后面有介绍),因此可以从命令行中对组件进行编译。
VisualStudio.NET提供的功能远不只是一个编译器。
它为开发ASP.NET应用程序(WebForms)和Windows应用程序(WindowsForms)提供了一个丰富的环境,其中包括像拖放、语句完成(Intellisense)和调试等所有常用功能。
这是一个很好且高效的工具,但如果不喜欢,也可以不使用它。
另外,.NET语言的开放设计允许第三方提供支持.NET的其他编辑器。
3.2VisualBasic.NET
作为VisualBasic的最新版本,VisualBasic.NET在功能方面迈出了很大的一步,它新增的几项功能充分利用了CLS和CLR的特性。
语言的每次升级都在性能上有所提高。
为了理解VisualBasic.NET实现此性能跨越的原因,先来看看VisualBasic以前版本存在的一些问题:
VisualBasic运行时库:
这些是相对巨大的DLL,它们融合了各种基本功能,所有VisualBasic程序的运行都需要调用它们。
通常程序员所抱怨的就是这些DLL的庞大容量和版本化问题(不同版本的VB需要不同的库)。
您可能会认为这些被CLR取代了,但事实正好相反,CLR更甚于此。
不过尽管容量太大仍旧是一个问题,但可分布的CLR的容量大约18MB,而且支持多个版本。
面向对象的功能不强:
VisualBasic不提供继承性、重载等这样的面向对象功能。
这些功能有效,但这是由于COM的能力(而不是VisualBasic本身)产生了许多问题。
无法创建多线程应用程序:
随着MicrosoftTransactionServer(MTS)的引入,n层结构成为了现实标准,VisualBasic程序员开始掌握组件化的知识。
但是,VisualBasic组件必须放在ApartmentThreading模型中,而这个限制招致了想建立多线程组件的程序员的很多批评。
但我们认为许多批评都不太合适,因为并没有多少人能真正地写出一个完全线程化的组件。
实际上,管理线程、状态等并不是一件容易的事情。
而所有这些问题在VisualBasic.NET中都消失了。
由于运行时库由CLR来处理,因此不再需要它们,同时面向对象的功能得以大大增强(这部分要归功于CLR和CLS的支持),此外所有的线程问题都已不存在了。
有了CLR,就不必再考虑线程问题了(除非确实想这么做)。
下面就来看看VisualBasic.NET的一些新增功能。
3.2.1面向对象功能
OO(面向对象)功能可能是程序员最为强烈要求增加的功能之一。
当VisualBasic6发行测试版时,用户最常问到的一个问题就是它是否支持继承性。
由于这是CLS的一个固有功能,因此现在的VisualBasic.NET支持该功能,在默认情况下类也是可继承的。
实际上,整个.NET都是基于类的,有很大的灵活性,不仅可以扩展和重载自己的类,还可以对许多系统类这样做。
1.类
在以前的VisualBasic版本中,类是使用Class语句创建的。
不过,VisualBasic.NET中其语法有了一些变化:
[Public|Private|Protected|Friend|ProtectedFriend]
[Shadows]
[MustInherit|NotInheritable]ClassclassName
EndClass
说明:
为了代码行的连续性,VisualBasic仍然要求使用下划线(_)。
上面没有使用下划线,只是为了使该语法看起来更简洁明了。
表3-1展示了以上代码中关键字的含义。
表3-1关键字及其含义
关键字
含义
Public
该类可用于公共访问
Private
该类只能在声明它的文件中访问
Protected
该类只能从包含类或从包含类派生的类型中访问
Friend
该类只能从该程序集中访问
ProtectedFriend
该类只能从该程序或从包含类派生的类型中访问
Shadows
该类遮盖了基类中名称完全相同的类。
Shadows只能用于内部类、结构和接口
MustInherit
该类是一个抽象类,而且类成员必须由继承类来执行
NotInheritable
该类是不可继承的
在类中,成员定义遵守相同的规则。
默认情况下,没有用关键字显式声明的成员为Public类型。
例如:
PublicClassCalculator
'implementationgoeshere
EndClass
或者:
ProtectedMustInheritClassCalculator
'abstractimplementationgoeshere
EndClass
2.方法:
方法声明为一个Sub或一个Function,但是VisualBasic.NET中作出了一些改进以满足继承性规则。
Sub的语法如下所示:
[Overloads|Overrides|Overridable|NotOverridable|MustOverride|
Shadows|Shared]
[Private|Public|Protected|Friend|ProtectedFriend]
SubsubName[(parameters)]
EndSub
Function的语法为:
[Overloads|Overrides|Overridable|NotOverridable|MustOverride|
Shadows|Shared]
[Private|Public|Protected|Friend|ProtectedFriend]
FunctionfunctionName[(parameters)][Astype]
EndFunction
各关键字的含义如表3-2所示。
表3-2关键字及其含义
关键字
含义
Overloads
该成员被重载,同时存在一个以上的声明,且每个声明都有不同的参数。
在同一个类中重载方法时,不需要Overloads关键字,但如果使用了该关键字,就必须在所有的重载方法中使用它
Overrides
该成员重写了基类中一个相同名称的成员。
在需要为子类的某个成员提供自定义执行代码时,就可以使用这个关键字。
重写的方法必须有相同的签名,就是说参数和数据类型必须与基类成员的参数和数据类型相匹配
NotOverridable
该成员不能在派生类中重写
Overridable
该方法可由派生类重写
MustOverride
该成员必须在派生类中重写。
这里隐含了Overridable
Shadows
该方法遮蔽了父类中的一个方法。
这表明父类中的方法是不可用的,允许使用不同于父类中参数和数据类型的签名来创建方法。
它有效地重新声明了类型
Shared
该成员由类的所有实例共享,且独立于类实例。
这等同于C#或C++中的静态方法
Public
该成员可实现公共访问
Private
该成员只能在类中访问
Protected
该成员只能从包含类或从包含类派生的类型中访问
(续表)
关键字
含义
Friend
该成员只能从本程序中访问
ProtectedFriend
该成员只能从本程序或从包含成员派生的类型中访问
例如:
PublicClassCalculator
PublicFunctionAdd(Op1AsDouble,Op2AsDouble)AsDouble
ReturnOp1+Op2
EndFunction
EndClass
说明:
这是一个与以前函数语法很大的区别——现在使用Return关键字来返回函数值,而不像以前那样把值赋予函数名来实现。
3.属性
属性可以作为Public成员变量来执行,或者通过使用Property语句来执行。
例如:
PublicClassCalculator
PublicOp1AsDouble
PublicOp2AsDouble
PublicFunctionAdd()AsDouble
ReturnOp1+Op2
EndFunction
EndClass
类可以这样使用:
DimcalcAsNewCalculator
Calc.Op1=123
Calc.Op2=456
Response.Write(calc.Add())
上面的范例使用了公共变量。
另外还有一个首选方法是使用Property,其语法已变为:
[Default|ReadOnly|WriteOnly]PropertypropertyName([parameters])
[Astype]
Get
'codeforgettingtheproperty
EndGet
Set
'codeforsettingtheproperty
EndSet
EndProperty
定义为ReadOnly的属性只有Get块。
同样,只有Get块的属性必须定义为ReadOnly。
这也适用于WriteOnly和Set块。
另外这里不再有Let选项,因为Set提供了相同的功能。
例如:
PublicClassCalculator
Private_op1AsDouble
Private_op2AsDouble
PublicPropertyOperand1()AsDouble
Get
Operand1=_op1
EndGet
Set
_op1=value
EndSet
EndProperty
PublicPropertyOperand2()AsDouble
Get
Operand2=_op2
EndGet
Set
_op2=value
EndSet
EndProperty
EndClass
注意Set块中关键字value的用法。
您所写的代码应该和上面一样,因为value是一个包含了属性值的隐含变量。
4.默认属性及属性参数
默认属性同样也发生了变化,这是因为它们只由具有参数列表的属性支持。
例如,可以将Result属性添加到Calculator类中,以包含某次运算的最终结果,但是不能使它成为Default属性。
所以不能将代码写成下面这样:
DefaultPropertyResult()AsDouble
该代码会阻止下面的工作:
Label1.Text=MyCalc
为了声明默认属性,必须有参数(不能将这些参数声明为ByRef)。
如果想详细了解这方面的内容,请参阅.NETSDK附带的VisualBasic.NET相关文档。
5.构造函数和对象创建
Class_Initialize事件已经从类中删除了,取代它的是成员函数New,该函数允许继承构造函数。
在VisualBasic.NET中,一个很不错的新功能是重载的使用,它对于提供构造函数来说是很理想的。
New()方法是重载的一个特殊例子,这是由于它不需要Overloads关键字。
例如,下面是Person类:
PublicClassPerson
Private_firstNameAsString
Private_lastNameAsString
SubNew()
_firstName=""""
_lastName=""""
EndSub
SubNew(firstNameAsString,lastNameAsString)
_firstName=firstName
_lastName=lastName
EndSub
PublicPropertyFirstName()AsString
'propertycodehere
EndProperty
PublicPropertyLastName()AsString
'propertycodehere
EndProperty
EndClass
本例中出现了两个SubNew:
一个没有参数,而另一个有参数。
这说明我们可以编写如下语句:
DimcoolDudeAsNewPerson()
coolDude.FirstName=""Vince""
coolDude.LastName=""Patel""
或者:
DimcoolDudeAsNewPerson(""Vince"",""Patel"")
这就为使用类提供了一种更好的方法,而且简化了代码。
6.析构函数和对象析构
像Class_Initialize方法一样,Class_Terminate同样由Destruct方法取代了。
例如:
SubDestruct()
'codetocleanuphere
EndSub
和VisualBasic以前的版本比起来,VisualBasic.NET调用析构函数的方式有很大的不同,它是围绕着CLR来解决此问题的。
CLR一个不错的功能就是垃圾收集(GC),它在后台运行,负责收集不再使用的对象引用,这样程序员就不必为析构这些对象引用而操心。
但是,其缺点在于,由于它是一个后台任务,因此我们无法知道析构函数究竟在何时调用。
在发布VisualBasic.NET测试版的过程中,人们曾广泛讨论有关垃圾收集的问题,其结果是Microsoft发布了专门用来说明垃圾收集及其效果的文件(详情请搜索MSDN站点中有关DeterministicFinalization的内容)。
有些人还关心这种情况:
即当对象不再使用时,需要确保进行必要的处理(例如清理资源)。
如果是这种情况,最好创建一个包含此功能的方法,当使用完类实例时,就调用此方法。
事实上,释放对象实例和进行垃圾回收,这两者之间的时间差别非常小,这是因为垃圾收集器始终都在运行。
7.继承性
如上一章所述,.NET中的所有事物都是对象,因此可以继承几乎所有的事物。
如果将前面的Person类作为一个基类,就可以根据它来创建一个新类,方法如下:
PublicClassProgrammer
InheritsPerson
Private_avgHoursSleepPerNightAsInteger
PublicSubNew()
MyBase.New()
EndSub
PublicSubNew(firstNameAsString,lastNameAsString)
MyBase.New(firstName,lastName)
EndSub
PublicSubNew(firstNameAsString,lastNameAsString,_
hoursSleepAsInteger)
MyBase.New(firstName,lastName)
_avgHoursSleepPerNight=hoursSleep
EndSub
PublicPropertyAvgHoursSleepPerNight()AsInteger
Get
AvgHoursSleepPerNight=_avgHoursSleepPerNight
EndGet
Set
_avgHoursSleepPerNight=value
EndSet
EndProperty
EndClass
该类扩展了现有的Person类,并增加了一个新的属性,其方法为:
首先,在类的声明后是Inherits语句,指定要继承的基类。
PublicClassProgrammer
InheritsPerson
接着,定义了现有的构造函数。
由于该类要提供一个额外的构造函数,因此需要重载基类的构造函数。
请注意这些定义是如何与基类中的定义相匹配的,以及如何使用MyBase调用基类的构造函数。
这里没有改变现有的构造函数,只是添加了自己的构造函数,因此这里只是将功能映射到基类中。
PublicSubNew()
MyBase.New()
EndSub
PublicSubNew(firstNameAsString,lastNameAsString)
MyBase.New(firstName,lastName)
EndSub
现在就可以添加自己的构造函数了,它调用以前的构造函数,接着设置其他属性。
PublicSubNew(firstNameAsString,lastNameAsString,_
hoursSleepAsInteger)
MyBase.New(firstName,lastName)
_avgHoursSleepPerNight=hoursSleep
EndSub
最后,添加新属性的定义。
PublicPropertyAvgHoursSleepPerNight()AsInteger
Get
AvgHoursSleepPerNight=_avgHoursSlee