Visual Basic/習語
習語是一種表達想法的模板或通用方法。就像人類語言中的習語使說話者和聽眾的生活更輕鬆一樣,計算機程式設計中的良好習語使程式設計師的生活更輕鬆。當然,一種語言中具有習語的用法可能在另一種語言中不適用。
並不一定存在物理模板,習語可以存在於你的腦海中,並且非常熟悉,以至於當你遇到一個可以用習語解決的問題時,你會幾乎沒有注意到自己在做什麼就開始鍵入程式碼了。
另一個經常用於描述這種想法的詞是模式。然而,模式這個詞已經變得非常正式,並且與面向物件聯絡緊密,我認為更通用的詞習語更好,它也是我將要展示的第一個主要示例中通常使用的詞。
習語可以是特定於語言的、特定於庫的、特定於領域的,或者這三種的任何組合。例如,如果你正在編寫一個處理事物列表的程式,你可能會借鑑一些 Lisp 的列表處理習語,即使 Visual Basic Classic 本身沒有任何本地列表處理函式。透過建立操作列表的函式而不是內聯程式碼來完成它,你會使你的程式碼對閱讀它的人更有意義。
程式設計師感覺自己像上帝一樣,因為他們完全控制著他們的程式,這是司空見慣的事。我認為程式設計師應該效仿莎士比亞:當他那個時代的英語無法表達他想說的話時,他不猶豫地擴充套件了它的風格範圍和詞彙量。
這個習語的名字聽起來像一個非常複雜的概念,但實際上它非常簡單,並且非常適合 Visual Basic Classic。這個習語是解決諸如設定和清除忙標誌、控制對檔案的訪問、設定滑鼠圖示等等問題的一種方法。
基本思路是使用物件的建構函式來獲取或查詢對某個資源的鎖定,並使用解構函式來釋放該鎖定。在像 Visual Basic 這樣的具有確定性終結的語言中,這將產生非常簡潔的程式碼。
讓我們以滑鼠為例。
在 Windows 程式中,將滑鼠圖示設定為指示程式繁忙,使用者必須等待,這非常常見。當你有很多不同的程式碼片段可能需要很長時間,以及呼叫它們的許多不同的程式碼路徑時,就會出現問題。如果呼叫者不知道該程序將耗時很長,它就不會知道必須更改圖示,因此我們在長時間執行的例程中添加了更改滑鼠指標的程式碼。這在嘗試組合兩個或更多這樣的例程之前是有效的:哪個例程可以取消忙圖示?
一種常見的解決方案是將滑鼠視為一種資源,並使用一對例程來控制對它的訪問,這些例程維護一個計數。一個例程在例程設定忙圖示時增加計數,另一個例程在計數變為零時減少計數並清除圖示。這在發生異常之前是有效的,然後一個例程可能會提前退出,而減少計數的例程將不會被呼叫。當然,你可以在錯誤處理程式中新增另一個呼叫,但這會導致額外的維護工作。
更好的解決方案是利用 Visual Basic 中內建的自動終止事件。基本思路仍然是使用計數,但是你不用例程來保護計數,而是用物件來保護它。
每個想要透過顯示滑鼠忙圖示來表明它很耗時的過程都應該將它的程式碼封裝在一個 with 語句中,該語句透過呼叫 NewMouseBusy 來建立一個 cMouseBusy 例項。當例程結束時,cMouseBusy 例項將被自動銷燬,它的 Terminate 事件處理程式將被執行,計數將減少一。
因為程式可能打開了多個窗體,所以當設定忙圖示時,我們必須考慮到窗體,因為 MousePointer 屬性屬於窗體而不是應用程式
為了知道要更改哪個窗體的滑鼠指標,我們必須為每個不同的客戶端保留一個單獨的計數
- 編譯示例程式碼並檢查它是否有效。
- 擴充套件程式碼以允許使用多種型別的忙圖示。例如,帶有指示使用者介面仍然處於活動狀態的指標的圖示,以及沒有指標的圖示,指示使用者必須等待。
- 建立一個測試程式來演示新功能。
只需將此行作為每個應該顯示忙游標的函式的第一行新增
Dim oMousebusy As cMouseBusy: Set oMousebusy = NewMouseBusy(Client)
或將忙部分封裝在
with NewMouseBusy(Client)
...
End With
客戶端是任何具有 mouseicon 屬性的東西
此類的物件在透過名為NewMouseBusy的建構函式建立時獲取滑鼠忙狀態。它們在終止時釋放它。
忙狀態只是一個計數器,每次建立一個新的 cMouseBusy 物件時它就會增加,每次銷燬這樣的物件時它就會減少。如果計數增加到 1,那麼滑鼠忙圖示就會在客戶端物件上設定,當計數減少到 0 時,就會設定正常圖示。
VERSION 1.0 CLASS
BEGIN
MultiUse = -1 'True
Persistable = 0 'NotPersistable
DataBindingBehavior = 0 'vbNone
DataSourceBehavior = 0 'vbNone
MTSTransactionMode = 0 'NotAnMTSObject
END
Attribute VB_Name = "cMouseBusy"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = True
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = False
Option Explicit
Private moClient As Object
Friend Sub Initialize(ByRef roClient As Object)
Set moClient = roClient
If IncrefCount(roClient) = 1 Then
roClient.MousePointer = vbHourglass
End If
End Sub
Private Sub Class_Terminate()
If DecRefCount(moClient) = 0 Then
moClient.MousePointer = vbDefault
End If
End Sub
此模組提供 cMouseBusy 的建構函式。這使我們能夠在建立物件時向其傳遞引數。
Attribute VB_Name = "modMouseBusy"
Option Explicit
Private moReferenceCount As Dictionary
Public Function NewMouseBusy(ByRef roClient As Object) As cMouseBusy
If moReferenceCount is Nothing then
Set moReferenceCount = New Dictionary
End If
Set NewMouseBusy = New cMouseBusy
NewMouseBusy.Initialize roClient
請記住,Visual Basic 是單執行緒的,並且除非你呼叫 DoEvents,否則使用者介面在所有程式碼停止執行之前不會更新,因此這樣做以確保對滑鼠圖示的更改是可見的。
DoEvents
End Function
此函式為給定物件增加計數,並返回新的計數。
Public Function IncrefCount(ByRef roClient As Object) As Long
Dim lRefCount As Long
IncrefCount = 1 + moReferenceCount(roClient)
moReferenceCount(roClient) = IncrefCount
End Function
此函式減少計數,並返回新的計數。請注意,如果計數變為零,則該條目將從列表中刪除;這必須完成,因為鍵是物件,如果我們不釋放引用,它們將不會終止。
Public Function DecRefCount(ByRef roClient As Object) As Long
DecRefCount = moReferenceCount(roClient) - 1
If DecRefCount = 0 Then
moReferenceCount.Remove roClient
Else
moReferenceCount(roClient) = DecRefCount
End If
End Function
此窗體可讓你測試滑鼠忙類。只需單擊按鈕並觀察滑鼠游標。
VERSION 5.00
Begin VB.Form frmTestMouseBusy
Caption = "frmTestMouseBusy"
ClientHeight = 2175
ClientLeft = 60
ClientTop = 360
ClientWidth = 4140
LinkTopic = "Form1"
ScaleHeight = 2175
ScaleWidth = 4140
StartUpPosition = 3 'Windows Default
Begin VB.CommandButton Command2
Caption = "Command2"
Height = 735
Left = 2160
TabIndex = 1
Top = 480
Width = 1215
End
Begin VB.CommandButton Command1
Caption = "Command1"
Height = 735
Left = 480
TabIndex = 0
Top = 480
Width = 1215
End
End
Attribute VB_Name = "frmtestMouseBusy"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = False
Option Explicit
Private Sub xb1(ByRef delay As Long)
With NewMouseBusy(Me)
Dim t As Double
t = Timer
Do While t + delay > Timer
DoEvents
Loop
xBug
End With
End Sub
Private Sub xBug()
With NewMouseBusy(Me)
Dim t As Double
t = Timer
Do While t + 3 > Timer
DoEvents
Loop
Debug.Print 1 / 0
End With
End Sub
Private Sub Command1_Click()
On Error Resume Next
xb1 3
End Sub
Private Sub Command2_Click()
On Error Resume Next
xb1 5
End Sub
測試的專案檔案。請注意,你需要引用 Microsoft Scripting Runtime 庫來提供 Dictionary 類。
Type=Exe
Reference=*\G{00020430-0000-0000-C000-000000000046}#2.0#0#..\..\..\..\..\..\WINNT\System32\stdole2.tlb#OLE Automation
Reference=*\G{420B2830-E718-11CF-893D-00A0C9054228}#1.0#0#..\..\..\..\..\..\WINNT\System32\scrrun.dll#Microsoft Scripting Runtime
Class=cMouseBusy; cMouseBusy.cls
Module=modMouseBusy; modMouseBusy.bas
Form=frmtestMouseBusy.frm
Startup="frmTestMouseBusy"
HelpFile=""
Command32=""
Name="prjTestMouseBusy"
HelpContextID="0"
CompatibleMode="0"
MajorVer=1
MinorVer=0
RevisionVer=0
AutoIncrementVer=0
ServerSupportFiles=0
VersionCompanyName="ABB"
CompilationType=0
OptimizationType=0
FavorPentiumPro(tm)=0
CodeViewDebugInfo=0
NoAliasing=0
BoundsCheck=0
OverflowCheck=0
FlPointCheck=0
FDIVCheck=0
UnroundedFP=0
StartMode=0
Unattended=0
Retained=0
ThreadPerObject=0
MaxNumberOfThreads=1
| 上一頁:有效程式設計 | 目錄 | 下一頁:最佳化 Visual Basic |