| VS.NET开发环境内建了一个宏引擎和一个可扩展的对象模型,这个对象模型拥有近200个对象,使开发者对IDE环境的控制、利用能力达到一个新的高度,包括访问和操作当前工程结构、已打开窗口的集合、内建调试器等。利用这个对象模型可以构造出各种各样的工具,从简单的键击事件记录器,到本身拥有用户界面又能够与VS.NET IDE交互的复杂插件。在这篇文章中,我们要探讨的就是如何用宏来自动执行一些常见的代码生成任务,例如为类构造属性过程。
一、基础知识
宏是VS.NET提供的众多扩展工具中的一种,设计简单,但却可以收到出乎意料的好效果。与其他Microsoft产品的宏不同,VS.NET宏建立在完整.NET代码的基础上,这意味着VS.NET宏能够调用任意.NET类库,例如它能够读写XML文件、显示窗体,甚至能够访问远程Web服务。
对于简单的宏,可以通过在VS.NET编辑器中记录动作的方式获得。按照如下步骤操作:
在VS.NET中选择菜单“工具”→“宏”→“记录Temporary Macro”。VS.NET显示出宏记录器工具栏,并开始记录用户动作。
按正常的操作方式,执行要记录的动作,宏记录器将自动记录这些动作。
完成后,在宏记录器工具栏上点击“停止记录”按钮。
以后如果要重复执行刚才执行的动作,只需选择菜单“工具”→“宏”→“运行Temporary Macro”,或按热键Ctrl+Shift+P。
如果要查看宏的代码,选择菜单“工具”→“宏”→“宏资源管理器”。宏资源管理器显示出由各个宏模块构成的树以及每个宏模块包含的宏,如图一所示,每一个宏对应一个VS.NET过程。
图一
如果要编辑刚才通过记录方式获得的宏,在RecordingModule中右击TemporaryMacro,选择“编辑”。VS.NET显示出一个独立的IDE窗口,并装入宏代码供编辑(图二)。宏代码编辑窗口与原先的VS.NET编辑环境非常相似,甚至连资源管理器和动态帮助也差不多。VS.NET任意时刻只能保存一个临时宏(即TemporaryMacro),每次记录新的宏时,原来的临时宏就被覆盖。要想永久保留临时宏的代码,只要把它粘贴和复制到另一个宏过程即可。
图二
正如前面指出的,宏代码使用.NET本身的语法和类库,但除此之外,宏代码还可以访问一组VS.NET专门提供的可扩展对象,这些对象用来与窗口交互、插入或读取VS.NET编辑器内的文本,等等。例如,假设要在编辑器中增加一行内容,可使用下面的宏代码:
' 获得插入点(光标的位置)
Dim TS As TextSelection = DTE.ActiveDocument.Selection
' 移到行的末尾
TS.EndOfLine()
' 增加一个新的行(相当于在编辑器内按Enter键)
TS.NewLine()
' 插入一些文本
TS.Insert("Sample Text")
|
在 Visual Studio 中,DTE 对象是自动化对象模型的根。“DTE”是一个缩写,它表示 Development Tools Environment(开发工具环境)。ActiveDocument代表当前激活的文档,其Selection属性表示选定的文本。TextSelection对象支持对选定文本的访问。
所有这些对象包含在一个专用的EnvDTE名称空间内,EnvDTE名称空间包含在EnvDTE.dll程序集之内。所有宏工程默认自动引用该程序集。
学习编写VS.NET宏的捷径之一是先利用宏记录功能自动生成宏,然后查看该宏的代码。
二、自动生成属性过程
Visual Basic 6有几个自动生成类代码的插件,包括能够自动生成属性过程的向导。遗憾的是,VS.NET没有提供与之相似的功能,每次开始设计一个类的时候大家都会遇到这个问题。下面的代码是一个用来封装私有数据的属性过程,显然,输入这个属性过程远不如输入Public MyVar As String这个语句简单。
Public Property MyVar As String
Get
Return _MyVar
End Get
Set(ByVal Value As String)
_MyVar = Value
End Set
End Property
|
在VS.NET中,解决这个问题的办法就是创建一个宏,由宏自动根据需要生成属性过程,一种可行的思路是:构造一个简单的宏分析当前插入点的代码,然后生成完整的属性过程。首先我们为该宏创建一个私有的函数GetInsertion():根据一行代表私有变量声明的代码,创建相应的属性过程声明。在这里,我们假定私有变量总是以一个前导的下划线(“_”)开始,在属性过程中该下划线将被省略。当然,你可以根据自己的需要,对GetInsertion()函数的代码作一些调整。
Private Function GetInsertion(ByVal text As String) As String
Dim Words() As String = text.Trim.Split()
If Words.Length < 4 Then
' 该行内容不是一个合法的变量声明
Return ""
Else
Dim Insertion As String
Insertion = " Public Property " & Words(1).Trim("_")
Insertion &= " As " & Words(3)
Insertion &= vbNewLine
Insertion &= " Get"
Insertion &= vbNewLine
Insertion &= " Return " & Words(1)
Insertion &= vbNewLine
Insertion &= " End Get"
Insertion &= vbNewLine
Insertion &= " Set(ByVal Value As " & Words(3) & ")"
Insertion &= vbNewLine
Insertion &= " " & Words(1) & " = Value"
Insertion &= vbNewLine
Insertion &= " End Set"
Insertion &= vbNewLine
Insertion &= " End Property"
Insertion &= vbNewLine & vbNewLine
Return Insertion
End If
End Function
|
接下来创建一个宏使用上面的函数。下面的宏首先选中当前行,利用GetInsertion()函数生成一个属性过程,把该属性过程插入到当前行的后面:
Public Sub ExpandPrivateMembersFromLine()
Dim TS As TextSelection = DTE.ActiveDocument.Selection
Dim Insertion As String
Insertion &= GetInsertion(TS.Text)
TS.EndOfLine()
TS.NewLine()
TS.Insert(Insertion)
End Sub
|
图三显示了宏资源管理器中列出的ExpandPrivateMembersFromLine宏,双击即可运行。
图三
现在,我们可以方便地扩展这个宏,使之适用于多个行。下面的ExpandPrivateMembersFromSelection() 宏把每一个选中的行传递给GetInsertion()函数(所有空行将被GetInsertion()函数自动忽略),把函数返回的文本组织起来,然后插入到编辑器。
Public Sub ExpandPrivateMembersFromSelection()
Dim TS As TextSelection = DTE.ActiveDocument.Selection
Dim Insertion As String, Line As String
Dim Lines() As String = TS.Text.Split(vbNewLine)
' 针对选中部分的每一行,返回相应的属性过程
For Each Line In Lines
Insertion &= GetInsertion(Line)
Next
TS.EndOfLine()
TS.NewLine()
TS.Insert(Insertion)
End Sub
|
以后,我们只需要输入“Private _MyVar As String”,然后运行任意一个宏,就可以自动生成相应的属性过程,这两个宏适用于任何基本的数据类型。
看到这里,有的读者肯定会想:是否有可能把这些宏写成语言中立的形式,使得这些宏适用于任意.NET语言。要达到这个目标,必须运用.NET框架的CodeDOM功能。使用CodeDOM是一个相当复杂的主题,这里就不再介绍了。
结束语:VS.NET宏是自动完成重复任务的强大工具。其实,只要运用得当,宏的功能还不局限于此,例如它可以用来强制执行编码标准、遵循优秀的编码规范、提高整个组织的一致性,例如利用宏生成标准的代码框架。 |