跳转到内容

Visual Basic/习语

来自维基教科书,开放的书籍,开放的世界

习语是一种表达想法的模板或通用方法。就像人类语言中的习语使说话者和听众的生活更轻松一样,计算机编程中的良好习语使程序员的生活更轻松。当然,一种语言中具有习语的用法可能在另一种语言中不适用。

并不一定存在物理模板,习语可以存在于你的脑海中,并且非常熟悉,以至于当你遇到一个可以用习语解决的问题时,你会几乎没有注意到自己在做什么就开始键入代码了。

另一个经常用于描述这种想法的词是模式。然而,模式这个词已经变得非常正式,并且与面向对象联系紧密,我认为更通用的词习语更好,它也是我将要展示的第一个主要示例中通常使用的词。

习语可以是特定于语言的、特定于库的、特定于领域的,或者这三种的任何组合。例如,如果你正在编写一个处理事物列表的程序,你可能会借鉴一些 Lisp 的列表处理习语,即使 Visual Basic Classic 本身没有任何本地列表处理函数。通过创建操作列表的函数而不是内联代码来完成它,你会使你的代码对阅读它的人更有意义。

程序员感觉自己像上帝一样,因为他们完全控制着他们的程序,这是司空见惯的事。我认为程序员应该效仿莎士比亚:当他那个时代的英语无法表达他想说的话时,他不犹豫地扩展了它的风格范围和词汇量。

资源获取即初始化 - RAII

[编辑 | 编辑源代码]

这个习语的名字听起来像一个非常复杂的概念,但实际上它非常简单,并且非常适合 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 属性的东西

cMouseBusy
[编辑 | 编辑源代码]

此类的对象在通过名为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
modMouseBusy.bas
[编辑 | 编辑源代码]

此模块提供 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
frmTestMouseBusy
[编辑 | 编辑源代码]

此窗体可让你测试鼠标忙类。只需单击按钮并观察鼠标光标。

  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
prjTestMouseBusy.vbp
[编辑 | 编辑源代码]

测试的项目文件。请注意,你需要引用 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
华夏公益教科书