|
編程人員從大量的程序開發(fā)中積累了許多非常實用的經(jīng)驗與技巧,它們就象一盤盤的快餐,看似簡單但營養(yǎng)絕對豐富!用“餐”之后,您的VB程序?qū)⒘⒓闯l。還猶豫什么,快來品嘗品嘗吧 ...
VB超頻快餐,讓我一次用個夠?。ㄒ唬?br>1.用Mid$命令超速字符串添加操作
2.從頭開始刪除集合項目 3.用InStr函數(shù)實現(xiàn)代碼減肥 4.精用Boolean表達式,讓代碼再減肥 5.函數(shù)名巧做局部變量 VB超頻快餐,讓我一次用個夠?。ǘ?br>6.火眼識破隱藏的Variant變量
7.GoSub在編譯程序中速度變慢 8.減少DoEvents語句的數(shù)量 9.And、Or和Xor:讓我們來優(yōu)化表達式 10.靜態(tài)變量慢于動態(tài)變量 VB超頻快餐,讓我一次用個夠?。ㄈ?br>11.善用"Assume No Aliasing"編譯選項
12.為常量定義合適的類型 13.你真正理解"Allow Unrounded Floating Point Operations"選項的含義嗎? 14.除法運算符"\"與"/"的區(qū)別 15.使用"$-類型"字符串函數(shù)會更快 VB超頻快餐,讓我一次用個夠!(四)
16.妙用Replace函數(shù)替代字符串連接操作符& 17.固定長度字符串數(shù)組:賦值快,釋放快! 18.未公開的返回數(shù)組型函數(shù)加速秘訣 19.深入使用LIKE操作符 20.創(chuàng)建任意長度重復字符串的簡潔方法 VB超頻快餐,讓我一次用個夠!(五)
21.另辟蹊徑處理字符串中的字符:字節(jié)數(shù)組法 22.快速清除數(shù)組部分內(nèi)容 23.快速初始化Variant和String類型數(shù)組 24.訪問簡單變量總是快于數(shù)組元素值 25.創(chuàng)建新表時,快速拷貝字段 VB超頻快餐,讓我一次用個夠!(六)
26.無閃爍地快速附加字符串到textbox控件 27.快速找到選中的OptionButton 28.表單及控件的引用阻止了表單的卸載 29.重定義編譯DLL文件的基地址 30.快速調(diào)入TreeView控件以及ListView控件的子項內(nèi)容 VB超頻快餐,讓我一次用個夠!(七)
31.Friend過程快于Public過程 32.使用Objptr函數(shù)快速查找集合中的對象 33.使用ObjPtr檢測2個對象變量是否指向同一對象 34.讀取文件內(nèi)容的簡潔方法 35.字體對象克隆招法 (一)
用Mid$命令超速字符串添加操作
大家都知道,&操作符的執(zhí)行速度是相當慢的,特別是處理長字符串時。當必須重復地在同一變量上附加字符時,有一個基于Mid$命令的技巧可
以使用?;舅悸肪褪牵侯A留一個足夠長的空間存放操作的結(jié)果。下面是應用這個技術的一個例子。
假設要建立一個字符串,它要附加從1開始的10000個整數(shù):"1 2 3 4 5 6 7 ... 9999 10000"。下面是最簡單的實現(xiàn)代碼:
res = "" For i = 1 to 10000: res = res & Str(i): Next 代碼雖然簡單,但問題也很明顯:Res變量將被重分配10000次。下面的代碼實現(xiàn)同樣的目的,但效果明顯好轉(zhuǎn):
Dim res As String
Dim i As Long Dim index As Long '預留足夠長的緩沖空間
res = Space(90000) '指針變量,指出在哪里插入字符串
index = 1 '循環(huán)開始
For i = 1 to 10000 substr = Str(i) length = Len(substr) '填充字符串的相應區(qū)間段數(shù)值
Mid$(res, index, length) = substr '調(diào)整指針變量
index = index + length Next
'刪除多余字符
res = Left$(res, index - 1) 測試表明:在一個333MHz的計算機上,前段代碼執(zhí)行時間為2.2秒,后者僅僅為0.08秒!代碼雖然長了些,可是速度卻提高了25倍之多。呵呵,
由此看來:代碼也不可貌相啊
從頭開始刪除集合項目 刪除集合中的所有內(nèi)容有許多方法,其中有些非常得迅速。來看看一個包含10,000個項目的集合:
Dim col As New Collection, i As Long For i = 1 To 10000 col.Add i, CStr(i) Next 可以從末尾位置為起點刪除集合內(nèi)容,如下:
For i = col.Count To 1 Step -1
col.Remove i Next
也可以從開始位置為起點刪除集合內(nèi)容,如下:
For i = 1 To col.Count Step 1
col.Remove i Next 試驗證明,后者要快于前者百倍多,比如0.06秒比4.1秒。原因在于:當引用接近末尾位置的集合項目時,VB必須要從第1個項目開始遍歷整個的項目鏈。
face=宋體>更有趣的是,如果集合項目的數(shù)量加倍,那么從末尾開始刪除與從頭開始刪除,前者要比后者花費的時間將成倍增長,比如前者是24秒,后者可能為0.12秒這么短! 最后提醒您:刪除集合的所有內(nèi)容的最快方法就是“毀滅”它,使用下面的語句: Set col = New Collection 對于一個包含20,000個項目的集合,上述操作僅僅耗時0.05秒,這比使用最快的循環(huán)操作進行刪除也要快2倍左右。
用InStr函數(shù)實現(xiàn)代碼減肥 可以采用“旁門左道”的方式使用Instr函數(shù)實現(xiàn)代碼的簡練。下面是一個典型的例子,檢測字符串中是否包含一個元音字母:
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來檢查變量的內(nèi)容。下面的例子檢查Word中是否包含一個季節(jié)的名字: 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代碼段,但一定要注意參數(shù)中的字符數(shù)目。下面的例子中,轉(zhuǎn)換數(shù)字0到9的相應英文名稱為阿拉伯數(shù)字: 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表達式,讓代碼再減肥
當設置基于表達式結(jié)果的Boolean型數(shù)值時,要避免使用多余的If/Then/Else語句結(jié)果。比如:
If SomeVar > SomeOtherVar Then
BoolVal = True
Else
BoolVal = False
End If
上面這段代碼就很煩瑣,它們完全可以使用下面的一行代碼來替代:
BoolVal = (SomeVar > SomeOtherVar)
括號不是必須的,但可以增加可讀性。根據(jù)表達式中的操作數(shù)不同,后者比前者執(zhí)行起來大約快50%到85%。后者中的括號對速度沒有影響。
有時,使用這個技術實現(xiàn)代碼的簡練并非很明顯。關鍵是要牢記:所有的比較操作結(jié)果或者是0(false),或者是-1(True)。所以,下面例
子中的2段代碼是完全相同的,但是第2段要運行得快些:
1、傳統(tǒng)方法:
If SomeVar > SomeOtherVar Then
x = x + 1
End If
2、更簡練的方法
x = x - (SomeVar > SomeOtherVar)
函數(shù)名巧做局部變量 很多程序員都沒有認識到“在函數(shù)本身中使用函數(shù)名”的妙處,這就象對待一個局部變量一樣。應用這個技巧可以起到臨時變量的作用,有時
還能加速程序運行。看看下面的代碼:
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變量,使用函數(shù)名稱本身這個局部變量,可以使程序更加簡練:
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類型。
許多開發(fā)者,特別是那些先前是C程序員的人,都會深信下面的語句將聲明2個Interger類型變量:
Dim x, y As Integer
而實際上,x被聲明為了variant類型。由于variant類型變量要比Integer類型慢很多,所以要特別注意這種情況。正確的一行聲明方法是:
Dim x As Integer, y As Integer
GoSub在編譯程序中速度變慢
編譯為本地代碼的VB應用程序中,如果使用 GoSubs 命令,就會比通常的 Subs 或者 Function 調(diào)用慢5-6倍;相反,如果是p-code模式,就會
相當快。
減少DoEvents語句的數(shù)量 不要在代碼中放置不必要的DoEvents語句,尤其是在時間要求高的循環(huán)中。遵循這個原則,至少能在循環(huán)中的每N次反復時才執(zhí)行DoEvents語句
,從而增強效率。比如使用下面的語句:
If (loopNdx Mod 10) = 0 Then DoEvents
如果只是使用DoEvents來屏蔽鼠標以及鍵盤操作,那么就可以在事件隊列中存在待處理項目時調(diào)用它。通過API函數(shù)GetInputState來檢查這個
條件的發(fā)生:
Declare Function GetInputState Lib "user32" Alias "GetInputState" () As Long
' ...
If GetInputState() Then DoEvents
為常量定義合適的類型 VB在內(nèi)部使用最簡單、最可能的數(shù)據(jù)類型保存符號數(shù)值,這意味著最通常的數(shù)字類型-比如0或者1-都按照Integer類型存儲。如果在浮點表達
式中使用這些常量,可以通過常量的合適類型來加速程序運行,就象下面的代碼:
value# = value# + 1#.
這個語句強迫編譯器按照Double格式存儲常量,這樣就省卻了運行時的隱含轉(zhuǎn)換工作。還有另外的一種處理方法就是:在常量聲明時就進行相
應類型的定義,代碼如下:
Const ONE As Double = 1
And、Or和Xor:讓我們來優(yōu)化表達式
要檢測一個整數(shù)值的最高有效位是否有數(shù)值,通常要使用如下的代碼(有二種情況:第一組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變量都是有符號的,因此,最高有效位也是符號位,不管處理什么類型的數(shù)值,通過下面的代碼就可以實現(xiàn)檢測目的:
If anyValue < 0 Then
' most significant bit is set
End If
另外,要檢測2個或者更多個數(shù)值的符號,只需要通過一個Bit位與符號位的簡單表達式就可以完成。下面是應用這個技術的幾段具體代碼:
1、判斷X和Y是否為同符號數(shù)值:
If (x < 0 And y < 0) Or (x >= 0 And y >=0) Then ...
' the optimized approach
If (x Xor y) >= 0 Then
2、判斷X、Y和Z是否都為正數(shù)
If x >= 0 And y >= 0 And z >= 0 Then ...
' the optimized approach
If (x Or y Or z) >= 0 Then ...
3、判斷X、Y和Z是否都為負數(shù)
If x < 0 And y < 0 And z < 0 Then ...
' the optimized approach
If (x And y And z) < 0 Then ...
4、判斷X、Y和Z是否都為0
If x = 0 And y = 0 And z = 0 Then ...
' the optimized approach
If (x Or y Or z) = 0 Then ...
5、判斷X、Y和Z是否都不為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 ...
然而我們可以輕易地證明他們是不同的,比如X=3(二進制=0011),Y=4(二進制=0100)。不過沒有關系,遇到這種情況時,我們可以對
上面的代碼進行局部優(yōu)化,就能實現(xiàn)目的。代碼如下:
If (x <> 0) And y Then ...
(三) 態(tài)變量慢于動態(tài)變量
在過程中引用靜態(tài)局部變量要比引用常規(guī)局部動態(tài)變量慢2-3倍。要想真正地加速過程的執(zhí)行速度,最徹底的方法就是將所有的靜態(tài)變量轉(zhuǎn)換
為模塊級別變量。
這種方法的唯一不足是:過程很少是自包含的,如果要在其他工程中重用,就必須同時拷貝并粘貼這些模塊級別變量。
另外的一種處理方法是:在時間要求高的循環(huán)前,將靜態(tài)變量數(shù)值裝入動態(tài)變量中。
善用"Assume No Aliasing"編譯選項
據(jù)說,如果過程能夠2次或多次引用同樣的內(nèi)存地址,那么過程就會包含別名數(shù)值。一個典型的例子如下:
Dim g_GlobalVariable As Long
...
Sub ProcWithAliases(x As Long)
x = x + 1
g_GlobalVariable = g_GlobalVariable + 1
End Sub
如果傳遞給這個過程g_GlobalVariable變量,則將通過一個直接引用以及x參數(shù)兩種方式修改變量的數(shù)值2次。
別名數(shù)值經(jīng)常是不良編程習慣的產(chǎn)物,對于程序優(yōu)化有害無益。事實上,如果能夠完全確認應用程序從來沒有使用到別名變量,就可以打開"As
sume No Aliasing"高級編譯選項,這將告知編譯器沒有過程可以修改同一內(nèi)存地址,使編譯器產(chǎn)生更加有效率的匯編代碼。更特別的是,編譯
程序?qū)⒃噲D緩沖這些數(shù)據(jù)到CPU的寄存器中,從而明顯地加速了程序運行。
總結(jié)一下,當遇到以下情況時,就不會有別名數(shù)值:(1) 過程不引用任何全局變量 (2) 過程引用了全局變量,但從來不通過ByRef參數(shù)類型傳
遞同一變量給過程 (3) 過程含有多個ByRef參數(shù)類型,但從來不傳遞同一變量到其中的2個或者多個之中。
你真正理解"Allow Unrounded Floating Point Operations"選項的含義嗎? 來自微軟的資料鼓吹:高級優(yōu)化對話框中的所有編譯選項都被認為是不穩(wěn)定的,它們可能導致不正確的結(jié)果,甚至程序崩潰。對于其中的大多
數(shù),這種說法是正確的,但是經(jīng)常有一個叫做"Allow Unrounded Floating Point Operations"的選項能夠給予正確的結(jié)果,防止應用程序產(chǎn)生
bug。考慮下面的代碼段:
Dim x As Double, y As Double, i As Integer
x = 10 ^ 18
y = x + 1 ' this can't be expressed with 64 bits
MsgBox (y = x) ' 顯示 "True" (不正確的結(jié)果)
嚴格地說,由于X和Y變量不包含相同的數(shù)值,MsgBox將顯示False??蓡栴}是,由于數(shù)值1E18與1E18+1都以相同的64位浮點Double類型來表示
,它們最終包含了幾乎相同的數(shù)值,最后的MsgBox結(jié)果將是True。
如果打開了"Allow Unrounded Floating Point Operations"編譯選項,VB就能重用已在數(shù)學協(xié)處理器堆棧中的數(shù)值,而不是內(nèi)存中的數(shù)值(比
如:變量)。因為FPU堆棧具備80位的精度,因此就可以區(qū)分出這2個數(shù)值的不同:
' if the program is compiled using the
' "Allow Unrounded Floating Point Operations" compiler option
MsgBox (y = x) ' 顯示 "False" (正確的結(jié)果)
總結(jié)一下:當以解釋模式、或者編譯的p-code模式、或者編譯的native代碼模式但關掉"Allow Unrounded Floating Point Operations"選項
這3種方式運行一個程序時,所有浮點數(shù)字運算在內(nèi)部都以80位的精度進行處理。但如果有一個數(shù)值是存儲在64位Double變量中,結(jié)果就是接近
的了,并且,隨后使用那個變量的表達式也將產(chǎn)生近似的結(jié)果,而不是絕對正確的結(jié)果。
相反,如果打開"Allow Unrounded Floating Point Operations"編譯選項后運行一段native編譯代碼,在隨后的表達式中VB就經(jīng)常能重用內(nèi)部
的80位數(shù)值,而忽略存儲在變量中的當前數(shù)值。注意:我們并不能完全控制這個功能,VB也許對此生效,也許就不生效,這要取決于表達式的
復雜程度以及最初分配數(shù)值語句與隨后產(chǎn)生結(jié)果的表達式語句的距離遠近。
除法運算符"\"與"/"的區(qū)別 整數(shù)間執(zhí)行除法運算時,要使用 "\" 而不是 "/"。 "/" 運算符要求返回一個單一數(shù)值,所以,表面上看似簡單的一行代碼:
C% = A% / B%
實際上包含了3個隱含的轉(zhuǎn)換操作:2個為除法運算做準備,從Integer轉(zhuǎn)換到Single;一個完成最后的賦值操作,從Integer轉(zhuǎn)換到Single。但
是如果使用了"\"操作符,情況就大不相同了!不僅不會有這么多中間步驟,而且執(zhí)行速度大大提高。
同時請記?。菏褂?/"操作符做除法運算時,如果其中之一是Double類型,那么結(jié)果就將是Double類型。所以,當2個Integer或者Single類型數(shù)
值做除法運算時,如果想得到高精度的結(jié)果,就需要手工強迫其中之一轉(zhuǎn)換為Double類型:
'結(jié)果為 0.3333333
Print 1 / 3
'結(jié)果為 0,333333333333333
Print 1 / 3#
使用"$-類型"字符串函數(shù)會更快
VB官方文檔似乎很鼓勵使用"無$"類字符串函數(shù),比如:Left、LTrim或者UCase,而不是實現(xiàn)同樣功能的Left$、LTrim$和UCase$函數(shù)。但是我
們必須認識到:前者返回variant類型的數(shù)值,當用于字符串表達式中時,最終必須要轉(zhuǎn)換為字符串(string)類型。
因此,在嚴格要求時間的代碼段中,我們應該使用后者,它們將快5-10%。
(四)
妙用Replace函數(shù)替代字符串連接操作符&
你大概不知道Replace函數(shù)還能這么用吧?比如下面的語句:
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)用&符號連接在一起。結(jié)果是:長長的字符連接串
變得難于閱讀。但是,使用Replace函數(shù),可以巧妙地解決這個問題。方法就是:將非打印字符以字符串中不出現(xiàn)的一個可打印字符表示,這樣
完整地寫出整個字符串,然后使用Replace函數(shù)替換那個特別的打印字符為非打印字符(比如:回車符vbCr)。代碼如下:
MsgBox Replace("Disk not ready.§§Please check that the diskette is in the " _
& "drive§and that the drive's door is closed.", "§", vbCr)
固定長度字符串數(shù)組:賦值快,釋放快!
固定長度字符串的處理速度通常慢于可變長度字符串,這是因為所有的VB字符串函數(shù)和命令只能識別可變長度字符串。因此,所有固定長度字
符串比然被轉(zhuǎn)換為可變長度字符串。
但是,由于固定長度字符串數(shù)組占據(jù)著一塊連續(xù)的內(nèi)存區(qū)域,因此在被分配以及釋放時,速度明顯快于可變長度的數(shù)組。比如:在一個Pentium
233MHz機器上,對于一個固定長度為100,000的數(shù)組,給其中30個位置分配數(shù)值,大約只花費半秒種的時間。而如果是可變長度的數(shù)組,同樣的
操作要耗費8秒之多!后者的刪除操作耗時大約0.35秒,但固定長度的數(shù)組幾乎可以立即“斃命”!如果應用程序中涉及到這么大的一個數(shù)組操
作,選擇固定長度方式數(shù)組絕對是確定無疑的了,無論是分配數(shù)值,還是釋放操作,都可以風馳電掣般完成。
未公開的返回數(shù)組型函數(shù)加速秘訣 在VB6中,函數(shù)是能夠返回數(shù)組對象的。這種情況下,我們不能象返回對象或者數(shù)值的其他函數(shù)一樣使用函數(shù)名當做局部變量來存儲中間結(jié)果,
因此不得不生成一個臨時局部數(shù)組,函數(shù)退出前再分配這個數(shù)組給函數(shù)名,就象下面的代碼一樣:
' 返回一個數(shù)組,其中含有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
' 以隨機數(shù)填充數(shù)組,并計算總和
Randomize Timer
For i = 1 To n
res(i) = Rnd
sum = sum + res(i)
Next
' 賦值結(jié)果數(shù)組,計算平均值
GetRandomArray = res
avg = sum / n
End Function
難以置信的是,只需要簡單地顛倒最后2條語句的順序,就能使上面這段程序變得快些:
' ... ' 賦值結(jié)果數(shù)組,計算平均值
avg = sum / n
GetRandomArray = res
End Function
例如,在一個Pentium II 333MHz 機器上,當N=100,000時,前段程序運行時間為0.72秒,后段程序則為0.66秒,前后相差10%。
原因何在呢?前段程序中,VB將拷貝res數(shù)組到GetRandomArray對應的結(jié)果中,當數(shù)組很大時,花費的時間是很長的。后段程序中,由于GetRan
domArray = res是過程的最后一條語句,VB編譯器就能確認res數(shù)組不會被再使用,因此將直接交換res和GetRandomArray的地址數(shù)值,從而節(jié)
省了數(shù)組元素的物理拷貝操作以及隨后的res數(shù)組釋放操作。
總結(jié)如下:當編寫返回數(shù)組的函數(shù)時,一定要將分配臨時數(shù)組到函數(shù)名的語句放在最后,就是其后緊挨者Exit Function 或者End Function的
位置。
深入使用LIKE操作符
在VB中,相對于本身的潛在功能,LIKE可能是最被忽視的一個操作符了。它的最基本用途是檢查一個字符串與給定樣式的匹配程度。比如,很
容易檢查一個產(chǎn)品ID號是否由一個字母以及3個數(shù)字組成:
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"
同理,也可以檢查一個字符串只包含數(shù)字:
If Not ProductID Like "*[!0-9]*" Then Print "OK"
下面的語句檢查一個字符串只包含字母或者數(shù)字符:
If Not ProductID Like "*[!A-Za-z0-9]*" Then Print "OK"
下面的語句檢查一個字符串是否為合法的VB變量名,就是說,開頭是一個字母,接著跟隨字母或數(shù)字符:
' VarName是被檢查的字符串
If VarName like "[A-Za-z]*" And Not Mid$(VarName, 2) Like "*[!A-Z_a-z0-9]*" _
Then Print "OK"
下面的語句檢查字符串是否至少包含2個空格(連續(xù)的或者間隔的):
If TestString Like "* * *" Then Print "OK"
而下面的語句進一步確認2個空格是不連續(xù)的:
If TestString Like "* ?* *" Then Print "OK"
檢查一個有符號整數(shù)是很復雜的事情,因為必須計算出前導符號并且計算好"#"符號的正確數(shù)目:
' NumValue是被檢測的字符串
If NumValue Like "#" Or (Len(NumValue) > 1 And NumValue Like "[-+0-9]" & _
String$(Len(NumValue) - 1, "#")) Then Print "OK"
最后一個例子是:檢測NumValue是否包含一個有符號的十進制數(shù)值。這種情況下,我們必須要確定存在一個前導符號以及只有一個十進制分隔
符,并且所有其他字符都是數(shù)字符:
' 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"
創(chuàng)建任意長度重復字符串的簡潔方法
String$函數(shù)只能重復復制單字符,當需要重復復制2個或多個字符時,就需要一個循環(huán)。看起來是否很麻煩?然而,使用以下的函數(shù)就能解決
這個問題?;舅悸肥牵航⒁粋€空格字符串,其長度為要重復復制的數(shù)目,然后替換每一個空格為要復制的字符串:
Function ReplicateString(Source As String, Times As Long) As String
ReplicateString = Replace$(Space$(Times), " ", Source)
End Function
但是請注意:根據(jù)字符串的長度以及重復的數(shù)目,這個方法也許比傳統(tǒng)的循環(huán)方法要慢些。
(五)
另辟蹊徑處理字符串中的字符:字節(jié)數(shù)組法
當要處理字符串中的每一個字符時,可以將字符串賦值到一個byte數(shù)組中進行操作。要記?。好恳粋€Unicode字符對應雙字節(jié)。這種方法通常要
快許多,因為節(jié)省了大量的Mid$函數(shù)操作以及大量的臨時字符串空間。下面的代碼是統(tǒng)計字符串中空格數(shù)目的最快方法
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()函數(shù)的特殊用法:在VB4(32位)、VB5和VB6中它返回數(shù)值2, 在VB4(16位)中返回數(shù)值1。因此,我們就可以使用同
一代碼段,而無需#If編譯指令。
快速清除數(shù)組部分內(nèi)容
清除動態(tài)數(shù)組的最快方法是使用ReDim,清除靜態(tài)數(shù)組則是使用刪除。但是如果只想清除數(shù)組的一部分內(nèi)容,怎么辦呢?看上去似乎只能使用Fo
r-Next循環(huán)了。
如果處理的是數(shù)字數(shù)組,有一個較快的方法。它基于ZeroMemory API函數(shù),正如函數(shù)名所示,它能將一塊內(nèi)存區(qū)域填充為0。看看怎么應用這個
函數(shù)來清除一個Long類型數(shù)組的一部分內(nèi)容:
Private Declare Sub ZeroMemory Lib "kernel32" Alias "RtlZeroMemory" (dest As _
Any, ByVal Bytes As Long)
'定義數(shù)組,填充數(shù)據(jù)
Dim a(1000) As Long
For i = 1 To UBound(a)
a(i) = i
Next
'從a(200)開始清除100個元素內(nèi)容
ZeroMemory a(200), 100 * Len(a(1))
請注意上述代碼中清除長度的表示方法,使用Len()函數(shù)保證了當數(shù)組類型改變時代碼仍然有效。
快速初始化Variant和String類型數(shù)組 VB中沒有提供定義數(shù)組并同時初始化其內(nèi)容的方法,所以大多數(shù)情況下,必須單獨地設置每一個元素,就象下面一樣:
Dim strArray(0 To 3) As String
strArray(0) = "Spring"
strArray(1) = "Summer"
strArray(2) = "Fall"
strArray(3) = "Winter"
在VB4、VB5和VB6中,可以使用Array()函數(shù)隨意創(chuàng)建一個Variants類型數(shù)組:
Dim varArray() As Variant
varArray() = Array("Spring", "Summer", "Fall", "Winter")
但卻沒有同樣的函數(shù)能創(chuàng)建非Variant類型數(shù)組。但是我們發(fā)現(xiàn),在VB6中可以使用Split()函數(shù)創(chuàng)建字符串數(shù)組:
Dim varArray() As String
'由Split建立的數(shù)組下標通常是從0開始的
varArray() = Split("Spring;Summer;Fall;Winter", ";")
在VB6中,同樣能充分利用函數(shù)返回數(shù)組的能力,創(chuàng)建數(shù)組初始化程序段。比如下面的代碼段:
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
同時,也可以創(chuàng)建一個子程序段來檢測傳遞給它的數(shù)值的類型,并返回正確類型的數(shù)組。這種情況下,函數(shù)應該定義為返回Variant。
訪問簡單變量總是快于數(shù)組元素值
讀寫數(shù)組中的元素速度通常都慢于訪問一個簡單變量,因此,如果在一個循環(huán)中要重復使用同一數(shù)組元素值,就應該分配數(shù)組元素值到臨時變
量中并使用這個變量。下面舉一個例子,檢測整數(shù)數(shù)組中是否存在重復項:
Function AnyDuplicates(intArray() As Integer) As Boolean
'如果數(shù)組包含重復項,返回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)到非數(shù)組變量中
value = intArray(i)
For j = i + 1 To lastItem
If value = intArray(j) Then
AnyDuplicates = True
Exit Function
End If
Next
Next
'沒有發(fā)現(xiàn)重復項
AnyDuplicates = False
End Function
上述程序有2層循環(huán),通過緩存intArray(i)的數(shù)值到一個普通的、非數(shù)組變量中,節(jié)省了CPU運行時間。經(jīng)測試,這將提高80%的速度。
創(chuàng)建新表時,快速拷貝字段
在VB6中,無需離開開發(fā)環(huán)境就可以創(chuàng)建新的SQL Server和Oracle表。方法很簡單:打開DataView窗口,用鼠標右鍵單擊數(shù)據(jù)庫的表文件夾,再
選擇新表格菜單命令。
當處理相似表格時,就是說具有許多相同字段的表格,我們完全可以在很短的時間內(nèi)容完成設定操作。具體步驟是:在設計模式下打開源表格
,加亮選擇要拷貝字段對應的行,按Ctrl-C拷貝信息到粘貼板;然后,在設計模式打開目標表格,將光標置于要粘貼字段所在的位置,按Ctrl-
V。
這樣,就拷貝了所有的字段名稱以及它們所帶的屬性。
(六)
無閃爍地快速附加字符串到textbox控件
附加文本到TextBox或者RichTextBox控件的通常方法是在當前內(nèi)容上連接上新的字符串:
Text1.Text = Text1.Text & newString
但還有一個更快的方法,并且會減少連接操作的閃爍感,代碼如下:
Text1.SelStart = Len(Text1.Text)
Text1.SelText = newString
快速找到選中的OptionButton
OptionButton控件經(jīng)常是作為控件數(shù)組存在的,要快速找到其中的哪一個被選中,可以使用下面的代碼:
'假設控件數(shù)組包含3個OptionButton控件
intSelected = Option(0).Value * 0 - Option(1).Value * 1 - Option(2).Value * 2
注意,因為第一個操作數(shù)總是0,所以上述代碼可以精簡如下:
intSelected = -Option(1).Value - Option(2).Value * 2
表單及控件的引用阻止了表單的卸載
當指派表單或者表單上的控件到該表單模塊以外的一個對象變量中時,如果要卸載表單,就必須首先將那個變量設置為 to Nothing。也就是說
,如果不設置為Nothing,即使看不到這個對象了,但它仍舊是保存在內(nèi)存中的。
注意:這并非是一個bug,這僅僅是COM引用規(guī)則的一個結(jié)果。唯一要注意的就是引用的這個控件將阻止整個表單的卸載操作,它將依賴于它的
父表單而存在。
重定義編譯DLL文件的基地址
許多VB開發(fā)者都知道應該在工程屬性對話框的“編譯”功能頁面中定義一個DLL基地址數(shù)值。這不同于工程中任何其他DLL或OCX的基地址。
當操作沒有源代碼的編譯DLL或者OCX文件時,可以使用EDITBIN程序修改它的基地址。EDITBIN程序隨Visual
Studio安裝后就有了,可以在主Visual Studio目錄的VC98\BIN目錄下找到它。比如,以下代碼重新設定一個編譯DLL文件的基地址為12000000
(16進制):
EDITBIN /REBASE:BASE=0x12000000 myfile.dll
同樣,EDITBIN程序?qū)蓤?zhí)行文件也有一些處理技巧。
以下是該程序支持的完整功能選項列表(使用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
快速調(diào)入TreeView控件以及ListView控件的子項內(nèi)容
有一個簡單但仍未發(fā)現(xiàn)的技巧可用于在TreeView控件中裝載多個節(jié)點,或者在ListView控件中裝載多個ListItems。這種方法要比傳統(tǒng)做法快。
先看看下面這個傳統(tǒng)方法:
For i = 1 To 5000
TreeView1.Nodes.Add , , , "Node " & i
Next
改進一下,代替重復引用TreeView1對象的Nodes集合,我們可以先將之保存在臨時對象變量中:
Dim nods As MSComctlLib.Nodes
Set nods = TreeView1.Nodes
For i = 1 To 5000
nods.Add , , , "Node " & i
Next
甚至,如果使用With代碼塊,還可以不需要臨時變量:
With TreeView1.Nodes
For i = 1 To 5000
.Add , , , "Node " & i
Next
End With
經(jīng)測試,優(yōu)化的循環(huán)代碼要比傳統(tǒng)方法執(zhí)行速度快40%左右。原因在于:將Nodes集合對象保存在臨時變量中,或者應用With代碼塊后VB將使用
隱藏的臨時變量后,就可以避免在循環(huán)中重復綁定Nodes對象到它的父TreeView1對象上。由于這種綁定是低效率的,因此省卻它就能節(jié)省大量
的執(zhí)行時間。
同樣的道理對于其他ActiveX控件也生效:
ListView控件的ListItems、ListSubItems以及ColumnHeaders集合 Toolbar控件的Buttons和ButtonMenus集合
ImageList的ListImages集合
StatusBar控件的Panels集合 TabStrip控件的Tabs集合 (七)
Friend過程快于Public過程 你可能會非常驚奇:Friend類型過程的執(zhí)行速度要明顯快于Public類型。這可以通過創(chuàng)建一個帶有Private類和Public類 (設定Instancing =
MultiUse)的ActiveX EXE工程看到,在2個類模塊中添加下面的代碼:
Public Sub PublicSub(ByVal value As Long)
'
End Sub
Public Function PublicFunction(ByVal value As Long) As Long
'
End Function
Friend Sub FriendSub(ByVal value As Long)
'
End Sub
Friend Function FriendFunction(ByVal value As Long) As Long
'
End Function
然后,在表單模塊中創(chuàng)建一個循環(huán),執(zhí)行每個例程許多次。比如,要在一個Pentium
II機器上查看執(zhí)行時間上的區(qū)別,可以調(diào)用每個例程1,000,000次。下面是測試的結(jié)果:
Private類模塊中,反復調(diào)用1,000,000次Public Sub或者Function耗費了0.46秒,而調(diào)用內(nèi)容相同的Friend類型模塊則分別只有0.05秒和0.06
秒。前后竟然相差了8-9倍之多!對于MultiUse類型的Public類模塊,也是一樣的結(jié)果。
對于這個不可思議的結(jié)果的可能解釋是:Friend型過程沒有處理匯集和拆裝代碼的消耗(Public過程可以從當前工程外被調(diào)用,因此COM必須要
來回地匯集數(shù)據(jù))。
但是在多數(shù)情況下,這些時間差別是不明顯的,特別是程序中包含一些復雜和耗時的語句時。
即使這樣,F(xiàn)riend型過程仍有其他的優(yōu)勢高于Public類型,比如:接受和返回在BAS模塊中定義的UDT變量的能力。
使用Objptr函數(shù)快速查找集合中的對象
ObjPtr函數(shù)的一個最簡單但是卻最有效的用途就是提供快速尋找集合中對象的關鍵字。假設有一個對象集合,它沒有可以當做關鍵字以從集合
中取回的屬性。那么,我們就可以使用ObjPtr函數(shù)的返回值作為集合中的關鍵字:
Dim col As New Collection
Dim obj As CPerson
'創(chuàng)建新的CPerson對象,并添加到集合中
Set obj = New CPerson
obj.Name = "John Smith"
col.Add obj, CStr(ObjPtr(obj)) '關鍵字必須是字符串
因為任何對象都有一個明確的ObjPtr數(shù)值,而且它是不變的,所以,我們可以容易地、快速地從集合中取回它:
' 刪除集合中的對象
col.Remove CStr(ObjPtr(obj))
這個技巧可以適用于任何類型的對象,包括VB中的表單和控件,以及外部對象。
使用ObjPtr檢測2個對象變量是否指向同一對象
判斷2個對象變量釋放指向同一對象的方法是使用Is操作符,代碼如下:
If obj1 Is obj2 Then ...
但當2個對象是同一類型時,或者指向同一個二級接口時,我們就可以利用ObjPtr()函數(shù)對代碼進行一些優(yōu)化處理:
If ObjPtr(obj1) = ObjPtr(obj2) Then ...
后者的執(zhí)行速度將比前種方法快40%多。但是請注意,2種方法原本就是很有效率的,只有在時間要求非常嚴格的上百成千次的循環(huán)中,才會體
現(xiàn)出這種差別。
讀取文件內(nèi)容的簡潔方法 讀取text文件的最快方法是使用Input$函數(shù),就象下面的過程:
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命令讀取文件每一行的方法快很多。下面是應用這個函數(shù)讀取Autoexec.bat的內(nèi)容到多行textbox控件的例子:
Text1.Text = FileText("c:\autoexec.bat")
但請注意:當文件包含Ctrl-Z(EOF)字符時,上面的函數(shù)代碼可能會發(fā)生錯誤。因此,要修改一下代碼:
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
' 讀取內(nèi)容,關閉文件
FileText = Space$(LOF(handle))
Get #handle, , FileText
Close #handle
End Function
字體對象克隆招法
當要應用一個控件的字體到另一控件時,最直接的方法就是直接賦值:
Set Text2.Font = Text1.Font
但多數(shù)情況下這種方法并不奏效,因為這實際上是將同一字體的引用分配給了2個控件。換言之,當隨后修改其中之一控件的字體時,另外一個
控件也受到影響。因此,要實現(xiàn)我們的目的,需要做的就是克隆字體對象并賦值給需要的控件。
最簡單的克隆字體的方法是手工地拷貝所有單獨的字體屬性,就象下面一樣:
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
'函數(shù)的應用
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
但是我們還能進一步地對代碼進行優(yōu)化,方法就是使用可被所有StdFont對象識別的隱藏IFont接口。這個接口具有一個Clone方法,用它就可以
精確地實現(xiàn)我們的目的。它以非正常方式執(zhí)行:創(chuàng)建一個克隆Font對象,然后返回相應的引用。這可能是實現(xiàn)克隆目的的最簡潔代碼了,而且
,執(zhí)行速度也是這里列舉的3種方法中最快的一個,要比使用PropertyBag對象的方法快大約3倍左右。來看看具體代碼:
Function CloneFont(Font As IFont) As StdFont
Font.Clone CloneFont
End Function
本文來自CSDN博客,轉(zhuǎn)載請標明出處:http://blog.csdn.net/sxycgxj/archive/2006/03/08/618531.aspx
|
|
|