.NET Framework 2.0中增加了一项新的诊断工具——“停表”StopWatch类,相比以前的计时系统,停表采用的是高精度计时方式,因此对性能测量更有帮助。为了试用StopWatch类,我选择了一个经典的测试题目,VB中的IIf函数。IIf函数与C系列的?:三元运算符十分相似,可以在一行语句内完成判断和赋值两项任务。但是,IIf是函数而不是运算符,其接受Object类型参数这一项已经够让人担忧的了。IIf到底有多慢呢?我们比较三个版本。首先是VB类库中的Object版:
Public Function IIf(Expression As Boolean , _ TruePart As Object , FalsePart As Object )As Object
第二个版本,是我编写的泛型版本的IIf,这个版本可以用于减少装箱拆箱过程带来的性能损失:
Public Function IIf(Of T)(Expression As Boolean , _ TruePart As T, FalsePart As T)As T If Expression Then Return TruePart Else Return FalsePart End If End Function
第三个版本当然是直接使用If语句。我们已经很清楚哪个比较快,但是没有一个量的概念,通过高精度计时的测试,可以让我看到函数内联化带来的收益是否够多。下面是计时和测试的代码:
Dim watch As New System.Diagnostics.StopwatchDim j As Integer = 0Dim f As Boolean = True 'Loop 10 times and get the average time For times As Integer = 1 To 10 watch.Start() For i As Integer = 0 To 1000000 j = Interaction.IIf(f , i, 5) Next watch.Stop()Next GC.Collect() TextBox1.AppendText("Object IIf: " & watch.ElapsedMilliseconds / 10 & vbCrLf) watch.Reset()For times As Integer = 1 To 10 watch.Start() For i As Integer = 0 To 1000000 j = IIf(f , i, 5) Next watch.Stop()Next GC.Collect() TextBox1.AppendText("Generic IIf: " & watch.ElapsedMilliseconds / 10 & vbCrLf) watch.Reset()For times As Integer = 1 To 10 watch.Start() For i As Integer = 0 To 1000000 If f Then j = i Else j = 5 Next watch.Stop()Next TextBox1.AppendText("Pure If: " & watch.ElapsedMilliseconds / 10 & vbCrLf)
我们看到StopWatch可以像真的停表一样开始,停止或继续,因此我们可以像在科学实验中计时一样使用它。而他的Elapsed属性可以提供各种单位的时间间隔。简单起见,我就用了毫秒。循环的次数不太多,取10次平均。最后的结果在我的机器上如下:
Object IIf: 219.6 Generic IIf: 51.5 Pure If: 9.7
纯If仅9.7毫秒的间隔也能测试出来,足见StopWatch的精度比原来的计时手段高多了。而结果也表明,函数调用的代价已经相当可观,但还不如装箱、拆箱带来的代价高。IIf当然是不要再用了,今后自己写函数时也应当注意,频繁调用的函数不能不考虑调用代价,内联化是不错的解决方案。
从原理上来说,My.Resources与前面介绍的My.Computer 或My.Application 是完全不同的,他带来的是另一种方便。My.Resource不是一个类库,而是My命名空间中唯一一个子命名空间。他的功能是什么呢?我们先回忆一下在.NET Framework1.1时代使用资源的情形。首先我们得通过工具,将图片、文本或声音等资源添加到资源列表中,编译成资源文件,再嵌入到我们的程序集中。当我们要使用资源的时候,必须通过System.Resources.ResourceManager从程序集中提取资源,然后自行判断资源的类型,做适当的转化并使用。比如从Form1的资源中取出ID为Greeting的字符串,需要写这么多代码:
Dim manager As New System.Resources.ResourceManage( _ "MyApp.Form1", Me .GetType().Assembly)Dim s As String = manager.GetString("Greeting")
而且那个编辑resx的界面不太直观,只添加字符串资源就不太方便,要想添加图片、音乐等文件到资源文件并在程序中取用就更麻烦了。到现在,许多Visual Basic .NET程序员还总是询问将图片音乐嵌入到EXE文件中去的方法。.NET Framework 2.0为解决这个问题引入了一个新特性——强类型资源。首先ResourceManager增加了一个GetStream()方法,方便获取图片、声音类的资源,其次Resgen工具可以根据资源的内容生成一个包装类,通过它可以直接强类型地读些程序集内的资源。而Visual Basic 2005将强类型资源与VB的IDE的特性结合在一起,就成了方便无比的My.Resources。
在实际使用My.Resources之前,我们先看看Visual Basic 2005新的资源编辑器,它现在已经继承到了项目属性中。打开项目属性并切换到Resources选项卡,我们可以看到如下的资源编辑器。
从顶端的下拉列表框中,可以选择资源的类型——声音、图像、文本、文件、图标或其他自定义内容。选择相应的类别,下面的编辑器就会发生变化,以适应当前类型的资源。每种类别都可以添加任意数量的资源。比如我们添加一个名称为Greeting的字符串,并让它的值等于"Hello",然后切换到代码窗口,输入My.Resource.看到什么了?Greeting弹出来了。现在我们使用这个资源之需要一行代码了。对比刚才所述的旧方法,你感到方便之处了吗?
Dim s As String = My .Resources.Greeting
如果我们添加了图片资源,那么也可以直接使用My.Resources来访问,而且就是BitMap类型,你想怎样用它就可以怎样用它,比如:
BackgroundImage = My .Resources.MyPic
一行代码就可以轻轻松松将窗口背景设置为资源中的图片。如果储存了声音资源,那么结合My.Computer.Audio的功能,播放资源中的声音也变得如此简单:
My .Computer.Audio.PlaySound(My .Resources.MySound)
想象一下以前要多少代码才能完成这样一个任务!有了My.Resource,资源的使用变得非常简单,你一定会改变对使用资源的看法,而爱上在自己的程序中使用资源的。
My.User是My命名空间中最小的成员,但是别看他小,功能对于.NET新手来说却不简单。如果你初次接触.NET开发,要获取当前登陆用户的用户名和用户组怎么办呢?谁会想到它其实是和Thread.CurrentPrincipal属性有关呢?My.User简单地将用户名和角色信息提供给你,要想获得当前登录的用户名,只需要输入My.User.Identity就行了。
下期预告
My命名空间的最后两个功能——My.Forms和My.WebServices
My.Computer可能是My命名空间中最有趣的部分了,这一部分封装了大量访问系统和硬件信息的功能,操作起来比直接使用.NET Framework或Windows API都方便得多。My.Computer中有很多对象,下面我们分别来介绍。
My.Computer.Audio
Audio对象提供了播放音频的功能,它既可以从wav等文件播放,也可以从音频数据流来播放,就是说你可以用它轻松播放储存在资源文件中或者数据库中的音频。播放时还可以指定后台播放或等待结束等多种设置。结合My.Resources来使用,更显得方便无穷。这是一个简单的播放wav文件的例子:
My .Computer.Audio.Play("c:\ding.wav", AudioPlayMode.BackgroundLoop)
My.Computer.Clipboard
Clipboard对象提供了以强类型方式读写剪贴板的功能,比Windows.Forms里面的剪贴板更加好用。使用Clipboard对象可以直接从剪贴板读写音频、图像、文本甚至我的电脑中的文件拖放信息。此外,由VB6升级的项目现在将直接使用My.Computer.Clipboard对象升级以前的Clipboard对象,这将解决VB.NET不能升级原先剪贴板功能的缺陷。下面的例子将文本框内的内容复制到剪贴板:
My .Computer.Clipboard.SetText(TextBox1.Text)
My.Computer.Clock
Clock对象是一个获取时间的工具,它可以直接获取当地时间、中时区的时间和从当时子时开始的毫秒计数。
My.Computer.FileSystem
这是微软Visual Basic Team在My.Computer中倾注最多精力的对象,使用它可以充分改善文件操作的复杂程度。FileSystem对象提供了易于理解的操作方式。FileSystem对象中复制文件的方法不但只需要指定目标路径,还可以帮助你建立目标目录中不存在的级别。它还特别提供了CopyDirectory的功能,可以复制整个目录!这正是目前.NET Framework缺乏的功能。同时FileSystem还能提供搜索上级目、子目录或根目录的功能,非常体贴。下面例子展示了如何在动画演示下将文件放入回收站。
My .Computer.FileSystem.DeleteFile("c:\mybigfile.big", True , True )
FileSystem对象还提供了只用一行代码就可以读取文本文件内容,或者将所需内容写入文本文件的功能,现在你不需要再用迷惑人的StreamReader、StreamWriter来读写文件了,还不用担心资源释放的问题。如下面的例子:
s = My .Computer.FileSystem.ReadAllText("c:\a.txt")
除了可以通过My访问以外,通过System.IO.FileSystem类也可以完成FileSystem对象的大多数功能,这种方式似乎更适合于使用C#或C++的开发者。
My.Computer.Info
看名字就知道了,这个对象的属性都是系统信息。如果你想获得本机物理内存或虚拟内存的总数,剩余量、操作系统名称、当前用户名、本机安装的文化设置等等,都可以轻松使用Info对象,它让你对应用程序所在的系统了如指掌。
My.Computer.Keyboard和My.Computer.Mouse
通过这两个对象,你可以快速获得用户键盘的信息,如大写锁定、数字键盘锁定等是否打开,以及鼠标有几个按键,是否配备滚轮等。如果你希望你的应用程序能够做到最体贴用户,那这些信息是少不了了。下面例子演示获取用户的鼠标左右键功能是否交换(这样你就可以知道用户是不是左撇子,从而提供更体贴的界面,多爽)
Dim f As Boolean = My .Computer.Mouse.ButtonsSwapped
My.Computer.Name
不用多说,这就是本机操作系统安装时输入的名称
My.Computer.Network
这个Network对象充分简化了最常用的网络任务,只需要一行代码,就可以Ping一个地址,或者检测网络是否接通。还能用一行代码下载或上传文件。比如这个例子就完成了一个下载文件的任务:
If My .Computer.Network.IsAvailable Then My .Computer.Network.DownloadFile("http://abc.com/x.zip ", _ "C:\download")End If My.Computer.Port
提供了用一行代码打开本机串口的功能,还能立刻绑定一个事件监视串口的变化。现在串口编程出奇的简单,再也不需要MSComm控件了。
My.Computer.Printers
这个Printers对象能够遍历本机所安装的所有打印机,还能找出默认的打印机。通过向默认打印机画图一样的操作,就能开始打印了。这样的操作会让你想起VB6时代便利而简洁的打印操作。下面的例子将在默认打印机上打印一个椭圆。从VB6升级项目时,原来的Printer对象将自动升级为My.Computer.Printers中的相关操作,升级的用户可以更加放心了。
My .Computer.Printers.DefaultPrinter.DrawEllipse( _ New RectangleF(2, 2, 50, 150), 1)My .Computer.Printers.DefaultPrinter.Print()
My.Computer.Registry
这个注册表对象可比Microsoft.Win32空间中的那个版本简单多了,他提供强类型的路径支持,还能非常方便地读写注册表。下面的例子是一段内置的代码片断,演示了如何判断某一键值是否存在。
Dim exists As Boolean = True
Dim path As String = "Software\Microsoft\TestApp\1.0"
If My .Computer.Registry.CurrentUser.OpenSubKey(path) Is Nothing Then exists = False End If
My.Computer.Screen
Screen对象可以获取屏幕的可视范围,像素的位数等。比VB6的Screen对象更强的是,它现在支持两个显示器。
现在我们已经了解了My.Computer中的所有对象,这些对象将大部分任务简化成一行代码,对你的日常编程是不是非常有帮助呢?
下期预告
下期我们介绍Visual Basic中的强类型资源——My.Resources
通过长时间分析Microsoft.VisualBasic.dll我发现了一个重要的问题:首先,Visual Basic语言运行时是要依靠Microsoft.VisualBasic.dll这个运行库的,其中类型转换、字符串比较和异常处理更是直接依靠该运行库中的类型。所以,Visual Basic开发应用程序无法不引用这个运行库。但是,大量的迹象表明,Microsoft.VisualBasic.dll是用Visual Basic开发的,不但Visual Basic的总设计师承认了这一点,运行库中的很多代码也体现了这一点,比如大量运用可选参数,以及使用了C#无法生成的try filter特性。那么是怎么用Visual Basic开发运行库自身的呢?它总不能引用还没有开发出来的自己吧?
另外一件事,我发现C#不允许将普通函数声明为类型的名字,比如Form1中不允许出现
public void Form1()
这样的声明。为什么呢?怕和构造函数弄混吗?但是构造函数没有返回类型,这是不可能弄混的啊。我用Visual Basic生成了与类型名一样的函数,然后拿到C#中调用,发现他能够很好地处理这种情况。那为什么不允许自己声明成这样呢?Visual Basic就没有这样的问题,虽然Visual Basic将New指定为构造函数的名字,但是却可以声明叫做New但不是构造函数的函数,只要这样写就行了。
Public Sub [New]()
语言设计者的想法,有时候很难捉摸。
在讨论My名字空间 的时候,IceShark 让我看了一个他写的类库,这是一个封装了大量Windows常见操作的类,和My的思想颇有异曲同工之妙。其中一个命名空间让我倍感兴趣:DesignPatterns.CreationalPatterns。竟然能将设计模式做成类库,这是我前所未见的。我特地看了一下他的Singleton类,只有一个Instance()静态方法,当然是为了获取唯一的实例用的。但Singleton类没有其他任何功能,我们要使用显然要继承它。但问题是,继承一个Singleton类还是Singleton吗?我们看看Instance()的实现方法(在此对作者表示歉意,原库是没有源代码的,我进行了一点反向工程):
public static Singleton Instance() { if (__Singleton == null ) __Singleton = new Singleton (); return __Singleton; }
清楚地看到,new是作用在Singleton上的,只能返回Singleton的实例,而不能返回他子类的实例。此外,子类的变量也不能引用父类的实例,所以继承这个Singleton类根本不能做成能用的Singleton。那我们究竟有没有办法实现继承后的类仍是Singleton呢,我觉得可以尝试一下泛型:
Public Class Singleton(Of T As {Singleton(Of T), New }) Private Shared _instance As T Private Shared _syncRoot As New Object
Public Shared Function GetInstnce() As T If _instance Is Nothing Then SyncLock _syncRoot If _instance Is Nothing Then _instance = New T End If End SyncLock End If Return _instance End Function End Class
现在我们可以试试继承这个类了:
Public Class SingSub Inherits Singleton(Of SingSub )
End Class
注意,我们这里为什么要约束基类自身,主要是保证T的取值范围,否则就会做成工厂类了。这里的SingSub类的确像是一个Singleton了,可以作用这种语法使用它:
Dim s As SingSub = SingSub.GetInstnce()
但是,又一个问题出现了,这里约束了New,就必须让子类有一个可公共访问的构造函数,这可与Singleton大相径庭。私有他的构造函数又不能采用这个约束。不知大家对这种情况有没有什么好的解决方法,或者说Singleton模式还是无法写成类库的。
关键字:VB8.0 VB.NET VB2005 My namespace
Visual Basic不同于Visual C#、Visual C++之处在于它更偏重于快速开发,更针对非专业开发人员和编程新手。Visual Basic 2005这次提供的“My”是一个极为出色的设计,可以帮助开发人员快速利用.NET Framework中的各种功能进行开发。说到My到底是什么,其实它就是一个工程相关的命名空间,其中的内容是由IDE帮助你组织的。
在My出现以前,.NET Framework已经具有强大而丰富的类库,学习这些类库算不上是一件轻松的事。许多VB或VC的开发者第一次接触到.NET开发时,总是习惯于自己实现或通过调用Windows API实现某些.NET早已准备好的功能。其原因就是.NET类库太庞大太分散了,许多常用的功能与那些不太常用的功能混在一起。比如,获得从当日零点开始的毫秒数的方法(经常被用来做随机数的种子)竟然与设置环境变量功能同在Environment类中,而不是“看上去像是”的System.Timers、TimeSpan或DateTime等命名空间或类型中。许多开发者对类库不熟悉,于是就一遍又一遍地重复开发.NET Framework的功能。Visual Basic Team为了解决这个问题,设计了My命名空间,它将.NET Framework中最常用的功能挑出来,然后按照最容易理解的逻辑结构存放在一起。当你深入My命名空间,你会发现那些功能就在你凭直觉就能想象到的路径中。
My命名空间在当前版本中主要包含My.Application、My.Computer、My.Resources、My.User、My.Forms和My.Webservices等六个主要部分。你可以输入My关键字找到他们,也可以导入My命名空间,其语法是:
Imports 项目名称 .My
在My命名空间中的所有类或对象中,My.Application是与当前运行的应用程序有关的对象,本次首先来介绍My.Application。My.Application提供的功能非常丰富,比如当前应用程序的主线程、主窗口、版本或公司版权等信息、文化和语言设置、路径及命令行、事件日志甚至Splash Screen的信息。下面的表格列出了My.Application的全部功能。
My.Application 成员
描述
ApplicationContext
应用程序的上下文,包括主线程和主窗体的信息
AssemblyInfo
程序集信息,包括版本、版权、标题、产品名称和可执行名称等
ChangeCurrentCulture
改变应用程序当前文化设置,如货币和时间的格式
ChangeCurrentUICulture
改变应用程序当前的用户界面文化设置,如显示语言和用词
CommandLineArgs
一个只读集合,返回当前应用程序的命令行参数。这些参数已经分隔开,无须像原来那样手工分隔Command函数的值了。
CurrentCulture
返回当前的文化设置
CurrentDirectory
返回应用程序使用的当前目录
CurrentUICulture
返回当前的用户界面文化设置
Deployment
返回按照ClickOnce方法部署的应用程序的Deployment对象
DoEvents
执行储存在Windows消息队列中的所有Windows消息
Exit
退出应用程序
GetEnvironmentVariable
通过环境变量的名字获取环境变量的值
IsNetworkDeployed
返回一个值,指示当前应用程序是否采用了网络部署方式
Log
一个记录应用程序事件日志和异常的日志工具
MainForm
当前应用程序的主窗体
OpenForms
当前应用程序中所有已经打开窗体的集合,与VB6的Forms集合功能相同
Run
启动Visual Basic的启动/关闭应用程序模式
SplashScreen
返回当前应用程序作为Splash Screen的窗口
可以注意到,My.Application中的某些功能和Application对象是一样的,但是My.Application不仅仅能用于Windows Form的应用程序,许多功能在控制台应用程序照样能够使用。下面举几个简单的例子来使用My.Application:
1、显示一个简单的关于窗口。
With My .Application.AssemblyInfo Dim msg As New System.Text.StringBuilder msg.AppendLine("Protuct Name: " & .ProductName ) msg.AppendLine("Company Name: " & .CompanyName ) msg.AppendLine("Version: " & .Version.ToString ) msg.AppendLine("Description: " & .Description ) MsgBox(msg.ToString, MsgBoxStyle.Information, "About " & .Title )End With
2、将当前打开的所有窗口的标题都改为环境变量%TITLE%的值
For Each f As Form In My .Application.OpenForms f.Text = My .Application.GetEnvironmentVariable ("TITLE") DoEvents() '也可以写成My.Application.DoEvents () Next
3、检查如果从网络上部署,修改当前用户界面文化设置为英语-美国
If My .Application.IsNetworkDeployed Then My .Application.ChangeCurrentUICulture ("en-US")End If
还有很多很多不同的用法,大家可以亲自试试。有了My.Application,设置和获取应用程序信息变得非常容易和有趣。这才是使用Visual Basic真正的感觉。
下期预告
和计算机硬件系统有关的My.Computer
网上经常有这样的问题:在设计控件时,怎样让自己的属性在属性窗口中显示的时候加一个“…”按钮或者下拉列表框,然后通过自定义的编辑器来编辑该属性的值。我举一个常见的例子,假设你的属性是表示一个路径,你希望在属性窗口中显示时可以有一个“…”按钮,单击之后显示一个目录选取对话框,可以通过它直接选取文件夹。
首先,要编写一个Editor类,继承自System.Drawing.Design.UITypeEditor:
Imports System.Security.PermissionsImports System.ComponentModelImports System.Drawing.DesignImports System.Windows.Forms
Public Class PathEditor Inherits UITypeEditor
<PermissionSet(SecurityAction.Demand, Name:="FullTrust")> _ Public Overrides Function EditValue(ByVal context As ITypeDescriptorContext, _ ByVal provider As IServiceProvider, ByVal value As Object ) As Object Using chooseDialog As New FolderBrowserDialog() With chooseDialog
.ShowNewFolderButton = False .Description = "Choose the folder of " & _ context.PropertyDescriptor.DisplayName .SelectedPath = CStr (value) .RootFolder = Environment.SpecialFolder.MyComputer .ShowDialog() Return .SelectedPath End With End Using End Function
<PermissionSet(SecurityAction.Demand, Name:="FullTrust")> _ Public Overrides Function GetEditStyle( _ ByVal context As ITypeDescriptorContext) As UITypeEditorEditStyle Return UITypeEditorEditStyle.Modal End Function
<PermissionSet(SecurityAction.Demand, Name:="FullTrust")> _ Public Overrides Function GetPaintValueSupported( _ ByVal context As ITypeDescriptorContext) As Boolean Return False End Function
End Class
在实现这个类时,应当重写UITypeEditor的几个方法。GetEditStyle方法返回一个数,指示编辑器采用外部的编辑器(显示“…”,并弹出窗口)还是使用下拉式列表。本例我们使用外部编辑器。如果不重写这个方法,默认是不会有任何特殊编辑方式的。GetPaintValueSupported方法返回是否支持对属性编辑器重画。我们经常看到颜色属性的编辑器可以直接显示颜色,图标属性的编辑器可以显示图标的缩略图等,都是通过这个功能完成。在这里我们不需要它。最后EditValue属性就是对编辑方式的具体定义。我们需要一个目录选择对话框来编辑目录,就可以在代码中建立它。value参数表示编辑此属性之前属性的值,contex参数则能提供一些有关属性本身的信息。
编写完毕以后,我们就可以给我们的属性绑定这个编辑器。用的是EditorAttribute,例子如下:
<Editor(GetType (PathEditor), GetType (UITypeEditor))> _Public Property PerlPath() As String Get Return _PerlPath End Get Set (ByVal value As String ) _PerlPath = value End Set End Property
这个EditorAttribute要接受两个参数,一个是Editor类的类型,就是刚才我们编写的那个类,一个必须是UITypeEditor本身的类型,用GetType运算符可以获得。现在,你的属性已经可以用自定义的编辑器编辑了。如图:
单击那个编辑按钮以后,弹出编辑器:
还有很多种编辑器,如对集合属性编辑的编辑器,对自定义结构编辑的编辑器等,都可以自由设计,然后通过编写Editor即可用于设计你自己的属性。
由于Visual Studio 2005意外挂掉,所以Visual Basic 2005新功能点评也只好推后。最近看了JDK1.5要给JAVA增加的新功能包括“静态导入”。功能就是将类型的静态成员导入,以便简化代码的书写。比如在JAVA中使用别人定义的常数一直是一个问题,JAVA中,常数都必须做成静态只读字段,因此使用常数都必须采用“类型名.常数名”的语法。由于JAVA允许在接口中定义静态字段,所以许多人将常数定义在接口中,再实现这个接口以避免输入类型名的麻烦,但这样不符合接口本来的功能,属于不良的设计。为此JAVA增加了静态导入,允许导入类型的静态成员,这样就不必写类型名了。
VB一直就没有这个问题,因为VB有模块,在模块里定义常数直接用名字就可以使用,无需类型名。因此许多人没发现VB有静态导入的功能。但是除了常数,我们有时候也想导入一些特定类的静态方法。最典型的就是Math类。在VB.NET刚推出的时候,许多人问Sin和Cos这些函数上哪里去了,其实就在那里,只不过必须写成Math.Sin,Math.Cos,这多麻烦呀。你可以用静态导入解决这个问题:
Imports System.Math
把这么简单的一句话放在文件开头,后面再用Sin、Cos之类的函数就不需要写Math了,直接用,就和内置函数一样方便。除了Math以外,我还常常静态导入System.Console,Application等,这样WriteLine或者DoEvents这样的常用方法就可以直接用了。
不过C#的开发人员不认为静态导入是一个值得加入的特征,所以C#至今没有静态导入。
.NET Framework 2.0 BETA的Console类加了很多新的属性和方法,现在可以改变背景颜色,字符颜色等等,还能移动控制台的缓冲区。如
Imports System.ConsoleImports System.Threading
Module Module1
Sub Main() BackgroundColor = ConsoleColor.Green ForegroundColor = ConsoleColor.Magenta Clear() WriteLine("Hello World")
For i As Integer = 1 To 20
MoveBufferArea(i - 1, i - 1, 1, 1, i, i) Thread.Sleep(100)
Next
Read() End Sub
End Module
效果如图:
安装了Visual Basic 2005 Express Edition Beta以后,我立刻被My的功能和新的项目属性窗口吸引了,泛型的智能感知也基本完善了。所以我后面将开始着重介绍这些最受人瞩目的功能。但是今天我还是按照计划,来介绍这个XML文档注释。
书写文档是程序员讨厌的任务之一,因为文档与代码的同步以前完全要靠手工进行。C#和Java引入的“注释文档”很大程度上简化了这个问题。它的基本原理就是将书写到代码中的特定注释提取出来,就可以生成代码的文档。现在,VB2005的XML文档注释给你的便利不仅仅如此,用了它你一定会爱上写文档。
Visual Basic 2005的文档注释是基于XML语法的,它可以描述代码中每一个类型和每一个成员的信息。在Visual Basic中只要输入'''(连续输入3个单引号)就可以输入XML格式的文档注释。比如
''' <summary> ''' 说明部分 ''' </summary> Public Class Form1
XML文档注释描述的是紧接注释部分的代码元素,一旦开始就不能断开,也不能掺杂其他代码元素,直到所有的XML标记被关闭。<summary>是VB推荐使用的标记,是该描述元素的摘要。除了<summary>,常用的标记有<remark>,表示说明;<param>表示参数信息;<exception>表示异常,此外还有<include>和<permission>等等。不一定非要使用这些推荐的标记,但是这些标记使用时可以自动完成,而且能被IDE所利用,如图所示:
XML文档注释写好以后,就可以用vbc.exe或者Visual Basic的IDE提取这些注释,生成所需的文档。你可以在项目属性对话框方便地找到生成文档的功能。下面就是生成文档的一部分,描述的是一个叫SubName的方法:
<member name="M:WindowsApplication1.Form1.SubName"> <summary> This is the summary of this member. </summary> <remarks></remarks> </member>
大写的M表示成员,而后面则是该成员的名称。有了这个XML文档,加上特定XSLT,就能生成一篇漂亮的报告。VB的编译器可以自动探测还没有编写文档的成员,还能检验特定成员的文档编写是否全面无误。
除了生成文档之外,书写XML文档注释最大的好处就是可以获得智能感知的支持。在VB5/6等早期版本,可以通过成员属性对话框为成员编写帮助,现在你只要写注释就行了。比如上面那个例子,当文档注释书写完毕后,再使用SubName的时候就会自动显示提示:
XML格式的文档提取出出来后将放到与程序集相同的文件夹中,这样其他项目甚至其他语言使用这些程序集时,也可以获得智能感知的提示。
XML文档注释的第二个好处是用来生成精美的报文。从“工具”菜单中选择“生成代码注释文档”就可以了,这样生成的报文能够快速察看所写代码的所有成员及说明。
XML文档注释是许多VB程序员盼望的功能,有许多第三方插件实现此功能,不过当然是官方的好用啦。下期介绍My命名空间。
最近在博客园 看到了多篇讨论C#“接口显式实现”的贴子,显式实现主要有两个功能:处理接口成员重名和隐藏实现接口的成员,还有一些与CLR内部原理有关的功能。如果VB程序员需要处理接口成员重名或者想隐藏实现接口的成员,可能也想要C#的接口显式实现功能,但是VB不提供显式实现的功能,换之以自由方式处理。
VB实现接口用的是Implements语句指定所实现的接口成员,因此实现方法本身的名字和访问级别是任意的。假如Interface1和Interface2都有一个叫Test的成员,而开发者需要将实现Interface1.Test的方法设为Public,而实现Interface2的方法隐藏(Private),就可以这么写:
Public Sub Test() Implements Interface1.TestEnd Sub Private Sub Test2() Implements Interface2.TestEnd Sub
这种做法在C#里已经无法实现了。而VB甚至可以让实现接口成员的方法为Protected或者Friend访问级别,还可以任意添加Overridable使它成为虚方法。所有这些都是自由的,不像C#接口显式实现那样受到约束。关于这条特征的原理,请参见《VB.NET是怎样做到的》 。
此外,博客园最近改良了代码着色工具 ,使它能够折叠代码。这让我太羡慕了,不知道是不是只支持C#。目前VB.NET代码着色功能都不太完善,很多地方上色不对,而且不支持将标识符、字符串、运算符等分别着色,控制力不强。因此我想开发一个VB.NET的代码着色工具,基于微软的Visual Basic .NET Parser 。但是我对折叠代码这方面不太熟,希望得到熟悉这方面的大虾的帮助。
这篇文章介绍了运算符重载的语法、用途,还介绍了许多运算符重载的使用规则和技巧。如果对运算符重载有兴趣,可以参考这篇文章:
Operator Overloading in Visual Basic 2005
关键字:VB2005 VB8.0 VB.NET Whidbey unsigned types
BASIC语言的类型系统在其发展过程中是不断完善的。实用派的Microsoft BASIC系列首先给Visual Basic增加了整型类型,VB又增加了Date型和Currency型;;VB4中加入了Boolean、Object和Byte类型;VB5加入了Decimal类型;VB.NET又加入了Char类型和更大的整型。VB.NET的类型系统依靠.NET Framework类型已经非常完善。但是在Visual Basic的发展史上从没有支持过无符号整数,而C系列的语言则对他们有良好的支持。因此Visual Basic在与C/C++编写的代码进行交互时总是会遇到障碍。.NET Framework支持无符号整型,但是他们不符合CLS,Visual Basic .NET也没有支持他们。事实上,无符号整型用处也不是很大,只是在进行平台交互的时候常会用到他们。不管怎么说,Visual Basic 2005现在支持他们,你可以用他们进行计算,就像其他类型一样。
Visual Basic 2005支持的无符号整型和有符号的SByte类型都是对.NET Framework中现有支持的无符号整型结构体的映射。下表列出了这些新类型的取值范围和映射的结构体:
Visual Basic中的名称
字面符号
取值范围
映射的结构
UShort
US
0~65535
System.UInt16
UInteger
UI
0~4294967295
System.UInt32
ULong
UL
0~18446744073709551615
System.UInt64
SByte
无
-128~127
System.SByte
字面符号是用来表示这些类型字面常量的,比如100可以表示有符号整数,也可以表示无符号,但是100UI就只能表示UInteger的100。对无符号整数的运算超出他们的取值范围默认会发生溢出异常,而将无符号整数转换为相应大小的有符号整数时,若所表示的数超过相应的有符号整数所能表示的范围,也会发生溢出异常,使用时应当注意。
Visual Basic 2005还引入了几个新的类型转换运算符,用于其他类型到无符号类型的强制类型转换。他们是CUShort,CUInt,CULng和CSByte。他们的用法与其它类型转换运算符是一样的。如:
Dim ui As UInteger = 100UIDim us As UShort = CUShort(ui) '显式转换 Dim ul As ULong = ui '隐式转换
注意无符号整型到同样大小的有符号整型不是扩大转换,而是缩小转换,在Option Strict完全打开的状态下只能进行显式转换。
Visual Basic 2005还支持创建基于无符号整型的枚举类型,只要在枚举定义的时候指定即可。比如指定基于ULong类型的枚举:
Public Enum MyEnum As ULong A B CEnd Enum
这时,MyEnum类型就和ULong类型一样,他的变量可以取任何ULong能取的数值。而A、B和C变量也都是实质为ULong类型的常数。
无符号整型最常用在与平台进行交互的地方,比如COM交互或调用平台的API。比如调用Win32的API函数:
Public Class windowsMessage Private Declare Auto Function mb Lib "user32.dll" Alias "MessageBox" _ (ByVal hWnd As Integer , _ ByVal lpText As String , _ ByVal lpCaption As String , _ ByVal uType As UInteger ) As Integer Private Const MB_OK As UInteger = 0 Private Const MB_ICONEXCLAMATION As UInteger = &H30 Private Const IDOK As UInteger = 1 Private Const IDCLOSE As UInteger = 8 Private Const c As UInteger = MB_OK Or MB_ICONEXCLAMATION Public Function messageThroughWindows() As String Dim r As Integer = mb(0, "Click OK if you see this!", _ "Windows API call", c) Dim s As String = "Windows API MessageBox returned " _ & CStr (r)& vbCrLf & "(IDOK = " & CStr (IDOK) _ & ", IDCLOSE = " & CStr (IDCLOSE) & ")" Return s End Function End Class
下期预告
XML文档注释