阅读:1358回复:0
VB超频快餐
除法运算符"\"与"/"的区别
整数间执行除法运算时,要使用 "\" 而不是 "/"。 "/" 运算符要求返回一个单一数值,所以,表面上看似简单的一行代码: C% = A% / B% 实际上包含了3个隐含的转换操作:2个为除法运算做准备,从Integer转换到Single;一个完成最后的赋值操作,从Integer转换到Single。但是如果使用了"\"操作符,情况就大不相同了!不仅不会有这么多中间步骤,而且执行速度大大提高。 同时请记住:使用"/"操作符做除法运算时,如果其中之一是Double类型,那么结果就将是Double类型。所以,当2个Integer或者Single类型数值做除法运算时,如果想得到高精度的结果,就需要手工强迫其中之一转换为Double类型: ’结果为 0.3333333 Print 1 / 3 ’结果为 0,333333333333333 Print 1 / 3# 使用"$-类型"字符串函数会更快 VB官方文档似乎很鼓励使用"无$"类字符串函数,比如:Left、LTrim或者UCase,而不是实现同样功能的Left$、LTrim$和UCase$函数。但是我们必须认识到:前者返回variant类型的数值,当用于字符串表达式中时,最终必须要转换为字符串(string)类型。 因此,在严格要求时间的代码段中,我们应该使用后者,它们将快5-10%。 妙用Replace函数替代字符串连接操作符; 你大概不知道Replace函数还能这么用吧?比如下面的语句: MsgBox "Disk not ready." ; vbCr ; vbCr ; _ "Please check that the diskette is in the drive" ; vbCr ; _ "and that the drive’s door is closed." 可以看出,为了显示完整的字符串含义,要将可打印字符与非打印字符(比如:回车符vbCr)用;符号连接在一起。结果是:长长的字符连接串变得难于阅读。但是,使用Replace函数,可以巧妙地解决这个问题。方法就是:将非打印字符以字符串中不出现的一个可打印字符表示,这样完整地写出整个字符串,然后使用Replace函数替换那个特别的打印字符为非打印字符(比如:回车符vbCr)。代码如下: MsgBox Replace("Disk not ready.§§Please check that the diskette is in the " _ ; "drive§and that the drive’s door is closed.", "§", vbCr) 固定长度字符串数组:赋值快,释放快! 固定长度字符串的处理速度通常慢于可变长度字符串,这是因为所有的VB字符串函数和命令只能识别可变长度字符串。因此,所有固定长度字符串比然被转换为可变长度字符串。 但是,由于固定长度字符串数组占据着一块连续的内存区域,因此在被分配以及释放时,速度明显快于可变长度的数组。比如:在一个Pentium 233MHz机器上,对于一个固定长度为100,000的数组,给其中30个位置分配数值,大约只花费半秒种的时间。而如果是可变长度的数组,同样的操作要耗费8秒之多!后者的删除操作耗时大约0.35秒,但固定长度的数组几乎可以立即“毙命”!如果应用程序中涉及到这么大的一个数组操作,选择固定长度方式数组绝对是确定无疑的了,无论是分配数值,还是释放操作,都可以风驰电掣般完成。 未公开的返回数组型函数加速秘诀 在VB6中,函数是能够返回数组对象的。这种情况下,我们不能象返回对象或者数值的其他函数一样使用函数名当做局部变量来存储中间结果,因此不得不生成一个临时局部数组,函数退出前再分配这个数组给函数名,就象下面的代码一样: ’ 返回一个数组,其中含有N个随即元素 ’ 并且将平均值保存在AVG中 Function GetRandomArray(ByVal n As Long, avg As Single) As Single() Dim i As Long, sum As Single ReDim res(1 To n) As Single ’ 以随机数填充数组,并计算总和 Randomize Timer For i = 1 To n res(i) = Rnd sum = sum + res(i) Next ’ 赋值结果数组,计算平均值 GetRandomArray = res avg = sum / n End Function 难以置信的是,只需要简单地颠倒最后2条语句的顺序,就能使上面这段程序变得快些: ’ ... ’ 赋值结果数组,计算平均值 avg = sum / n GetRandomArray = res End Function 例如,在一个Pentium II 333MHz 机器上,当N=100,000时,前段程序运行时间为0.72秒,后段程序则为0.66秒,前后相差10%。 原因何在呢?前段程序中,VB将拷贝res数组到GetRandomArray对应的结果中,当数组很大时,花费的时间是很长的。后段程序中,由于GetRandomArray = res是过程的最后一条语句,VB编译器就能确认res数组不会被再使用,因此将直接交换res和GetRandomArray的地址数值,从而节省了数组元素的物理拷贝操作以及随后的res数组释放操作。 总结如下:当编写返回数组的函数时,一定要将分配临时数组到函数名的语句放在最后,就是其后紧挨者Exit Function 或者End Function的位置。 深入使用LIKE操作符 在VB中,相对于本身的潜在功能,LIKE可能是最被忽视的一个操作符了。它的最基本用途是检查一个字符串与给定样式的匹配程度。比如,很容易检查一个产品ID号是否由一个字母以及3个数字组成: If ProductID Like "[A-Za-z][0-9][0-9][0-9]" Then Print "OK" ’ this is equivalment, because "#" stands for a digit If ProductID Like "[A-Za-z]###" Then Print "OK" 除了上述基本功能,LIKE在其他情况下也非常有用。以下就一一列举: 比如,可以检查一个字符串只包含大写字母: If Not ProductID Like "*[!A-Z]*" Then Print "OK" 同理,也可以检查一个字符串只包含数字: If Not ProductID Like "*[!0-9]*" Then Print "OK" 下面的语句检查一个字符串只包含字母或者数字符: If Not ProductID Like "*[!A-Za-z0-9]*" Then Print "OK" 下面的语句检查一个字符串是否为合法的VB变量名,就是说,开头是一个字母,接着跟随字母或数字符: ’ VarName是被检查的字符串 If VarName like "[A-Za-z]*" And Not Mid$(VarName, 2) Like "*[!A-Z_a-z0-9]*" _ Then Print "OK" 下面的语句检查字符串是否至少包含2个空格(连续的或者间隔的): If TestString Like "* * *" Then Print "OK" 而下面的语句进一步确认2个空格是不连续的: If TestString Like "* ?* *" Then Print "OK" 检查一个有符号整数是很复杂的事情,因为必须计算出前导符号并且计算好"#"符号的正确数目: ’ NumValue是被检测的字符串 If NumValue Like "#" Or (Len(NumValue) > 1 And NumValue Like "[-+0-9]" ; _ String$(Len(NumValue) - 1, "#")) Then Print "OK" 最后一个例子是:检测NumValue是否包含一个有符号的十进制数值。这种情况下,我们必须要确定存在一个前导符号以及只有一个十进制分隔符,并且所有其他字符都是数字符: ’ NumValue是被检测的字符串 If NumValue Like "#" Or (Len(NumValue) > 1 And Left$(NumValue, _ 1) Like "[-+.0-9]" And Not Mid$(NumValue, 2) Like "*[!.0-9]*" And Not _ NumValue Like "*.*.*" ) Then Print "OK" 创建任意长度重复字符串的简洁方法 String$函数只能重复复制单字符,当需要重复复制2个或多个字符时,就需要一个循环。看起来是否很麻烦?然而,使用以下的函数就能解决这个问题。基本思路是:建立一个空格字符串,其长度为要重复复制的数目,然后替换每一个空格为要复制的字符串: Function ReplicateString(Source As String, Times As Long) As String ReplicateString = Replace$(Space$(Times), " ", Source) End Function 但是请注意:根据字符串的长度以及重复的数目,这个方法也许比传统的循环方法要慢些。 另辟蹊径处理字符串中的字符:字节数组法 当要处理字符串中的每一个字符时,可以将字符串赋值到一个byte数组中进行操作。要记住:每一个Unicode字符对应双字节。这种方法通常要快许多,因为节省了大量的Mid$函数操作以及大量的临时字符串空间。下面的代码是统计字符串中空格数目的最快方法 Dim b() as Byte, count As Integer b() = source$ For i = 0 to UBound(b) Step LenB("A") If b(i) = 32 Then count = count + 1 Next 请注意上面代码中LenB()函数的特殊用法:在VB4(32位)、VB5和VB6中它返回数值2, 在VB4(16位)中返回数值1。因此,我们就可以使用同一代码段,而无需#If编译指令。 快速清除数组部分内容 清除动态数组的最快方法是使用ReDim,清除静态数组则是使用删除。但是如果只想清除数组的一部分内容,怎么办呢?看上去似乎只能使用For-Next循环了。 如果处理的是数字数组,有一个较快的方法。它基于ZeroMemory API函数,正如函数名所示,它能将一块内存区域填充为0。看看怎么应用这个函数来清除一个Long类型数组的一部分内容: Private Declare Sub ZeroMemory Lib "kernel32" Alias "RtlZeroMemory" (dest As _ Any, ByVal Bytes As Long) ’定义数组,填充数据 Dim a(1000) As Long For i = 1 To UBound(a) a(i) = i Next ’从a(200)开始清除100个元素内容 ZeroMemory a(200), 100 * Len(a(1)) 请注意上述代码中清除长度的表示方法,使用Len()函数保证了当数组类型改变时代码仍然有效。 快速初始化Variant和String类型数组 VB中没有提供定义数组并同时初始化其内容的方法,所以大多数情况下,必须单独地设置每一个元素,就象下面一样: Dim strArray(0 To 3) As String strArray(0) = "Spring" strArray(1) = "Summer" strArray(2) = "Fall" strArray(3) = "Winter" 在VB4、VB5和VB6中,可以使用Array()函数随意创建一个Variants类型数组: Dim varArray() As Variant varArray() = Array("Spring", "Summer", "Fall", "Winter") 但却没有同样的函数能创建非Variant类型数组。但是我们发现,在VB6中可以使用Split()函数创建字符串数组: Dim varArray() As String ’由Split建立的数组下标通常是从0开始的 varArray() = Split("Spring;Summer;Fall;Winter", ";") 在VB6中,同样能充分利用函数返回数组的能力,创建数组初始化程序段。比如下面的代码段: Function ArrayInt(ParamArray values() As Variant) As Integer() Dim i As Long ReDim res(0 To UBound(values)) As Integer For i = 0 To UBound(values) res(i) = values(i) Next ArrayInt = res() End Function 同时,也可以创建一个子程序段来检测传递给它的数值的类型,并返回正确类型的数组。这种情况下,函数应该定义为返回Variant。 访问简单变量总是快于数组元素值 读写数组中的元素速度通常都慢于访问一个简单变量,因此,如果在一个循环中要重复使用同一数组元素值,就应该分配数组元素值到临时变量中并使用这个变量。下面举一个例子,检测整数数组中是否存在重复项: Function AnyDuplicates(intArray() As Integer) As Boolean ’如果数组包含重复项,返回True Dim i As Long, j As Long, Dim lastItem As Long Dim value As Integer ’只计算机UBound()一次 lastItem = UBound(intArray) For i = LBound(intArray) To lastItem ’ 保存intArray(i)到非数组变量中 value = intArray(i) For j = i + 1 To lastItem If value = intArray(j) Then AnyDuplicates = True Exit Function End If Next Next ’没有发现重复项 AnyDuplicates = False End Function 上述程序有2层循环,通过缓存intArray(i)的数值到一个普通的、非数组变量中,节省了CPU运行时间。经测试,这将提高80%的速度。 创建新表时,快速拷贝字段 在VB6中,无需离开开发环境就可以创建新的SQL Server和Oracle表。方法很简单:打开DataView窗口,用鼠标右键单击数据库的表文件夹,再选择新表格菜单命令。 当处理相似表格时,就是说具有许多相同字段的表格,我们完全可以在很短的时间内容完成设定操作。具体步骤是:在设计模式下打开源表格,加亮选择要拷贝字段对应的行,按Ctrl-C拷贝信息到粘贴板;然后,在设计模式打开目标表格,将光标置于要粘贴字段所在的位置,按Ctrl-V。 这样,就拷贝了所有的字段名称以及它们所带的属性。 无闪烁地快速附加字符串到textbox控件 附加文本到TextBox或者RichTextBox控件的通常方法是在当前内容上连接上新的字符串: Text1.Text = Text1.Text ; newString 但还有一个更快的方法,并且会减少连接操作的闪烁感,代码如下: Text1.SelStart = Len(Text1.Text) Text1.SelText = newString 快速找到选中的OptionButton OptionButton控件经常是作为控件数组存在的,要快速找到其中的哪一个被选中,可以使用下面的代码: ’假设控件数组包含3个OptionButton控件 intSelected = Option(0).Value * 0 - Option(1).Value * 1 - Option(2).Value * 2 注意,因为第一个操作数总是0,所以上述代码可以精简如下: intSelected = -Option(1).Value - Option(2).Value * 2 表单及控件的引用阻止了表单的卸载 当指派表单或者表单上的控件到该表单模块以外的一个对象变量中时,如果要卸载表单,就必须首先将那个变量设置为 to Nothing。也就是说,如果不设置为Nothing,即使看不到这个对象了,但它仍旧是保存在内存中的。 注意:这并非是一个bug,这仅仅是COM引用规则的一个结果。唯一要注意的就是引用的这个控件将阻止整个表单的卸载操作,它将依赖于它的父表单而存在。 重定义编译DLL文件的基地址 许多VB开发者都知道应该在工程属性对话框的“编译”功能页面中定义一个DLL基地址数值。这不同于工程中任何其他DLL或OCX的基地址。 当操作没有源代码的编译DLL或者OCX文件时,可以使用EDITBIN程序修改它的基地址。EDITBIN程序随Visual Studio安装后就有了,可以在主Visual Studio目录的VC98\BIN目录下找到它。比如,以下代码重新设定一个编译DLL文件的基地址为12000000(16进制): EDITBIN /REBASE:BASE=0x12000000 myfile.dll 同样,EDITBIN程序对可执行文件也有一些处理技巧。 以下是该程序支持的完整功能选项列表(使用EDITBIN /? 可以列出这些): /BIND[:PATH=path] /HEAP:reserve[,commit] /LARGEADDRESSAWARE[:NO] /NOLOGO /REBASE[:[BASE=address][,BASEFILE][,DOWN]] /RELEASE /SECTION:name[=newname][,[[!]{cdeikomprsuw}][a{1248ptsx}]] /STACK:reserve[,commit] /SUBSYSTEM:{NATIVE│WINDOWS│CONSOLE│WINDOWSCE│POSIX}[,#[.##]] /SWAPRUN:{[!]CD│[!]NET} /VERSION:#[.#] /WS:[!]AGGRESSIVE |
|
|