如何让你的VB程序更神速
(创建时间:2012年03月04日 11:41:00)
狂野 : 

如何让你的VB程序更神速

编程人员从大量的程序开发中积累了许多非常实用的经验与技巧。其实,真正的好的程序代码只是在小的程序设计过程中得到体现。只要程序块得到了合理的优化,那么程序体的本质也就上升了一个级别。

请看下面关于实例的详细解说吧。

关于VB程序的提速问题,下面我将列举一些基本而又实际的注意事项:

1、  Mid$命令超速字符串添加操作
2
、从头开始删除集合项目
3
、用InStr函数实现代码减肥
4
、精用Boolean表达式,让代码再减肥
5
、函数名巧做局部变量
6
、火眼识破隐藏的Variant变量
7
GoSub在编译程序中速度变慢
8
、减少DoEvents语句的数量
9
AndOrXor:让我们来优化表达式
10
、静态变量慢于动态变量
11
、善用"Assume No Aliasing"编译选项
12
、为常量定义合适的类型
13
、你真正理解"Allow Unrounded Floating Point Operations"选项的含义吗?
14
、除法运算符"\""/"的区别
15
、使用"$-类型"字符串函数会更快
16
、妙用Replace函数替代字符串连接操作符&
17
、固定长度字符串数组:赋值快,释放快!
18
、未公开的返回数组型函数加速秘诀
19
、深入使用LIKE操作符
20
、创建任意长度重复字符串的简洁方法
21
、另辟蹊径处理字符串中的字符:字节数组法
22
、快速清除数组部分内容
23
、快速初始化VariantString类型数组
24
、访问简单变量总是快于数组元素值
25
、创建新表时,快速拷贝字段
26
、无闪烁地快速附加字符串到textbox控件
27
、快速找到选中的OptionButton
28
、表单及控件的引用阻止了表单的卸载
29
、重定义编译DLL文件的基地址
30
、快速调入TreeView控件以及ListView控件的子项内容
31
Friend过程快于Public过程
32
、使用Objptr函数快速查找集合中的对象
33
、使用ObjPtr检测2个对象变量是否指向同一对象
34
、读取文件内容的简洁方法
35
、字体对象克隆招法
36
、用Mid$命令超速字符串添加操作

下面请看详细解说:

 

大家都知道,&操作符的执行速度是相当慢的,特别是处理长字符串时。当必须重复地在同一变量上附加字符时,有一个基于Mid$命令的技巧可以使用。基本思路就是:预留一个足够长的空间存放操作的结果。下面是应用这个技术的一个例子。

假设要建立一个字符串,它要附加从1开始的10000个整数:"1 2 3 4 5 6 7 ... 9999

10000"
。下面是最简单的实现代码:
res = ""
For i = 1 to 10000: res = res & Str(i): Next

<face=
宋体>代码虽然简单,但问题也很明显:Res变量将被重分配10000次。下面的代码实现同样的目的,但效果明显好转:

Dim res As String
Dim i As Long
Dim index As Long

预留足够长的缓冲空间
res = Space(90000)

指针变量,指出在哪里插入字符串
index = 1

循环开始
For i = 1 to 10000
substr = Str(i)
length = Len(substr)

填充字符串的相应区间段数值
Mid$(res, index, length) = substr

调整指针变量
index = index + length

Next

删除多余字符
res = Left$(res, index - 1)

测试表明:在一个333MHz的计算机上,前段代码执行时间为2.2秒,后者仅仅为0.08秒!代码虽然长了些,可是速度却提高了25倍之多。由此看来:代码也不可貌相啊。

 

从头开始删除集合项目

删除集合中的所有内容有许多方法,其中有些非常得迅速。来看看一个包含10,000个项目的集合:
Dim col As New Collection, i As Long
For i = 1 To 10000
   col.Add i, CStr(i)
Next

可以从末尾位置为起点删除集合内容,如下:
For i = col.Count To 1 Step -1
col.Remove i
Next

也可以从开始位置为起点删除集合内容,如下:
For i = 1 To col.Count Step 1
col.Remove i
Next

<>
face=
宋体>试验证明,后者要快于前者百倍多,比如0.06秒比4.1秒。原因在于:当引用接近末尾位置的集合项目时,VB必须要从第1个项目开始遍历整个的项目链。 <>
face=
宋体>更有趣的是,如果集合项目的数量加倍,那么从末尾开始删除与从头开始删除,前者要比后者花费的时间将成倍增长,比如前者是24秒,后者可能为0.12秒这么短!
最后提醒您:删除集合的所有内容的最快方法就是毁灭它,使用下面的语句: Set col = New Collection

对于一个包含20,000个项目的集合,上述操作仅仅耗时0.05秒,这比使用最快的循环操作进行删除也要快2倍左右。

InStr函数实现代码减肥

可以采用旁门左道的方式使用Instr函数实现代码的简练。下面是一个典型的例子,检测字符串中是否包含一个元音字母:

1
、普通的方法:


If UCase$(char) = "A" Or UCase$(char) = "E" Or UCase$(char) = "I" Or UCase$(char) = "O" Or UCase$(char) = "U" Then

’ it is a vowel

End If


2
、更加简练的方法:

If InStr("AaEeIiOoUu", char) Then

’ it is a vowel

End If

同样,通过单词中没有的字符作为分界符,使用InStr来检查变量的内容。下面的例子检查Word中是否包含一个季节的名字: 1、普通的方法:

If LCase$(word) = "winter" Or LCase$(word) = "spring" Or LCase$(word) = _ "summer" Or LCase$(word) = "fall" Then

’ it is a season’s name

End If


2
、更加简练的方法:

If Instr(";winter;spring;summer;fall;", ";" & word & ";") Then

’ it is a season’s name

End If

有时候,甚至可以使用InStr来替代Select

Case
代码段,但一定要注意参数中的字符数目。下面的例子中,转换数字09的相应英文名称为阿拉伯数字: 1、普通的方法:


Select Case LCase$(word)

Case "zero"

result = 0

Case "one"

result = 1

Case "two"

result = 2

Case "three"

result = 3

Case "four"

result = 4

Case "five"

result = 5

Case "six"

result = 6

Case "seven"

result = 7

Case "eight"

result = 8

Case "nine"

result = 9

End Select


2
、更加简练的方法:

result = InStr(";zero;;one;;;two;;;three;four;;five;;six;;;seven;eight;nine;", _

";" & LCase$(word) & ";") \ 6 ?

 

 

精用Boolean表达式,让代码再减肥

当设置基于表达式结果的Boolean型数值时,要避免使用多余的If/Then/Else语句结果。比如:


If SomeVar > SomeOtherVar Then

BoolVal = True

Else

BoolVal = False

End If


上面这段代码就很烦琐,它们完全可以使用下面的一行代码来替代:


BoolVal = (SomeVar > SomeOtherVar)

括号不是必须的,但可以增加可读性。根据表达式中的操作数不同,后者比前者执行起来大约快50%85%。后者中的括号对速度没有影响。

有时,使用这个技术实现代码的简练并非很明显。关键是要牢记:所有的比较操作结果或者是0false),或者是-1True)。所以,下面例子中的2段代码是完全相同的,但是第2段要运行得快些:


1
、传统方法:

If SomeVar > SomeOtherVar Then

x = x + 1

End If


2
、更简练的方法

x = x - (SomeVar > SomeOtherVar)

函数名巧做局部变量

很多程序员都没有认识到在函数本身中使用函数名的妙处,这就象对待一个局部变量一样。应用这个技巧可以起到临时变量的作用,有时还能加速程序运行。看看下面的代码:

Function Max(arr() As Long) As Long

Dim res As Long, i As Long

res = arr(LBound(arr))

For i = LBound(arr) + 1 To UBound(arr)

If arr(i) > res Then res = arr(i)

Next

Max = res

End Function

去掉res变量,使用函数名称本身这个局部变量,可以使程序更加简练:

Function Max(arr() As Long) As Long

Dim i As Long

Max = arr(LBound(arr))

For i = LBound(arr) + 1 To UBound(arr)

If arr(i) > Max Then Max = arr(i)

Next

End Function

火眼识破隐藏的Variant变量

如果没有用As语句声明变量,默认类型就是Variants,比如:
Dim name ’ this is a variant
或者,当前模块下没有声明Option Explicit语句时,任何变量都是Variants类型。

许多开发者,特别是那些先前是C程序员的人,都会深信下面的语句将声明2Interger类型变量:

Dim x, y As Integer

而实际上,x被声明为了variant类型。由于variant类型变量要比Integer类型慢很多,所以要特别注意这种情况。正确的一行声明方法是:

Dim x As Integer, y As Integer

GoSub
在编译程序中速度变慢

编译为本地代码的VB应用程序中,如果使用 GoSubs 命令,就会比通常的 Subs 或者 Function 调用慢5-6倍;相反,如果是p-code模式,就会相当快。

 

 

减少DoEvents语句的数量

不要在代码中放置不必要的DoEvents语句,尤其是在时间要求高的循环中。遵循这个原则,至少能在循环中的每N次反复时才执行DoEvents语句,从而增强效率。比如使用下面的语句:



If (loopNdx Mod 10) = 0 Then DoEvents

如果只是使用DoEvents来屏蔽鼠标以及键盘操作,那么就可以在事件队列中存在待处理项目时调用它。通过API函数GetInputState来检查这个条件的发生:

Declare Function GetInputState Lib "user32" Alias "GetInputState" () As Long

’ ...

If GetInputState() Then DoEvents

为常量定义合适的类型

VB
在内部使用最简单、最可能的数据类型保存符号数值,这意味着最通常的数字类型-比如0或者1-都按照Integer类型存储。如果在浮点表达式中使用这些常量,可以通过常量的合适类型来加速程序运行,就象下面的代码:


value# = value# + 1#.

这个语句强迫编译器按照Double格式存储常量,这样就省却了运行时的隐含转换工作。还有另外的一种处理方法就是:在常量声明时就进行相应类型的定义,代码如下:

Const ONE As Double = 1

And
OrXor:让我们来优化表达式

要检测一个整数值的最高有效位是否有数值,通常要使用如下的代码(有二种情况:第一组If判断表明对Integer类型,第二组对Long类型):


If intValue And &H8000 Then

’ most significant bit is set

End If

If lngValue And &H80000000 Then

’ most significant bit is set

End If

但由于所有的VB变量都是有符号的,因此,最高有效位也是符号位,不管处理什么类型的数值,通过下面的代码就可以实现检测目的:


If anyValue < 0 Then

’ most significant bit is set

End If

另外,要检测2个或者更多个数值的符号,只需要通过一个Bit位与符号位的简单表达式就可以完成。下面是应用这个技术的几段具体代码:


1
、判断XY是否为同符号数值:


If
x*y>=0Then ...

’ the optimized approach

If (x Xor y) >= 0 Then


2
、判断XYZ是否都为正数


If x >= 0 And y >= 0 And z >= 0 Then ...

’ the optimized approach

If (x Or y Or z) >= 0 Then ...


3
、判断XYZ是否都为负数


If x < 0 And y < 0 And z < 0 Then ...

’ the optimized approach

If (x And y And z) < 0 Then ...


4
、判断XYZ是否都为0


If x = 0 And y = 0 And z = 0 Then ...

’ the optimized approach

If (x Or y Or z) = 0 Then ...


5
、判断XYZ是否都不为0


If x = 0 And y = 0 And z = 0 Then ...

’ the optimized approach

If (x Or y Or z) = 0 Then ...

要使用这些来简单化一个复杂的表达式,必须要完全理解boolean型的操作原理。比如,你可能会认为下面的2行代码在功能上是一致的:

If x <> 0 And y <> 0 Then

If (x And y) Then ...

然而我们可以轻易地证明他们是不同的,比如X3(二进制=0011),Y4(二进制=0100)。不过没有关系,遇到这种情况时,我们可以对上面的代码进行局部优化,就能实现目的。代码如下:

If (x <> 0) And y Then ...

静态变量慢于动态变量

在过程中引用静态局部变量要比引用常规局部动态变量慢23倍。要想真正地加速过程的执行速度,最彻底的方法就是将所有的静态变量转换为模块级别变量。


这种方法的唯一不足是:过程很少是自包含的,如果要在其他工程中重用,就必须同时拷贝并粘贴这些模块级别变量。


另外的一种处理方法是:在时间要求高的循环前,将静态变量数值装入动态变量中。



善用"Assume No Aliasing"编译选项

据说,如果过程能够2次或多次引用同样的内存地址,那么过程就会包含别名数值。一个典型的例子如下:



Dim g_GlobalVariable As Long

...

Sub ProcWithAliases(x As Long)

x = x + 1

g_GlobalVariable = g_GlobalVariable + 1

End Sub

如果传递给这个过程g_GlobalVariable变量,则将通过一个直接引用以及x参数两种方式修改变量的数值2次。

别名数值经常是不良编程习惯的产物,对于程序优化有害无益。事实上,如果能够完全确认应用程序从来没有使用到别名变量,就可以打开"Assume No Aliasing"高级编译选项,这将告知编译器没有过程可以修改同一内存地址,使编译器产生更加有效率的汇编代码。更特别的是,编译程序将试图缓冲这些数据到CPU的寄存器中,从而明显地加速了程序运行。


总结一下,当遇到以下情况时,就不会有别名数值:(1) 过程不引用任何全局变量 (2) 过程引用了全局变量,但从来不通过ByRef参数类型传递同一变量给过程 (3) 过程含有多个ByRef参数类型,但从来不传递同一变量到其中的2个或者多个之中。

 

读写文件 如果数据量大的时候 也会经常使的CPU使用达到100% 通过进行优化

读取文件内容的简洁方法

读取text文件的最快方法是使用Input$函数,就象下面的过程:

Function FileText (filename$) As String

Dim handle As Integer

handle = FreeFile

Open filename$ For Input As #handle

FileText = Input$(LOF(handle), handle)

Close #handle

End Function


使用上述方法要比使用Input命令读取文件每一行的方法快很多。下面是应用这个函数读取Autoexec.bat的内容到多行textbox控件的例子:

Text1.Text = FileText("c:\autoexec.bat")

但请注意:当文件包含CtrlZEOF)字符时,上面的函数代码可能会发生错误。因此,要修改一下代码:

Function FileText(ByVal filename As String) As String

Dim handle As Integer

判断文件存在性

If Len(Dir$(filename)) = 0 Then

Err.Raise 53 ’
文件没有找到

End If


binary模式打开文件

handle = FreeFile

Open filename$ For Binary As #handle

读取内容,关闭文件

FileText = Space$(LOF(handle))

Get #handle, , FileText

Close #handle

End Function

字体对象克隆招法

当要应用一个控件的字体到另一控件时,最直接的方法就是直接赋值:


Set Text2.Font = Text1.Font


但多数情况下这种方法并不奏效,因为这实际上是将同一字体的引用分配给了2个控件。换言之,当随后修改其中之一控件的字体时,另外一个控件也受到影响。因此,要实现我们的目的,需要做的就是克隆字体对象并赋值给需要的控件。


最简单的克隆字体的方法是手工地拷贝所有单独的字体属性,就象下面一样:


Function CloneFont(Font As StdFont) As StdFont

Set CloneFont = New StdFont

CloneFont.Name = Font.Name

CloneFont.Size = Font.Size

CloneFont.Bold = Font.Bold

CloneFont.Italic = Font.Italic

CloneFont.Underline = Font.Underline

CloneFont.Strikethrough = Font.Strikethrough

End Function


函数的应用

Set Text2.Font = CloneFont(Text1.Font)


如果使用VB6,就可以使用PropertyBag对象快速拷贝所有字体属性,并且代码会很简练、速度也快2倍:


Function CloneFont(Font As StdFont) As StdFont

Dim pb As New PropertyBag

拷贝字体到PropertyBag对象中

pb.WriteProperty "Font", Font

恢复字体对象到新控件

Set CloneFont = pb.ReadProperty("Font")

End Function


但是我们还能进一步地对代码进行优化,方法就是使用可被所有StdFont对象识别的隐藏IFont接口。这个接口具有一个Clone方法,用它就可以精确地实现我们的目的。它以非正常方式执行:创建一个克隆Font对象,然后返回相应的引用。这可能是实现克隆目的的最简洁代码了,而且,执行速度也是这里列举的3种方法中最快的一个,要比使用PropertyBag对象的方法快大约3倍左右。来看看具体代码:

Function CloneFont(Font As IFont) As StdFont

Font.Clone CloneFont


End Function

 

 

 

文档中心
可上传附件
选择
同时转发此条
回复
1楼
不错!我以前也是学过VB了,出来工作就把它还给老师了!唉!