什么时候在VBA中使用类?

时间:2020-03-06 14:34:41  来源:igfitidea点击:

什么时候适合在Visual Basic for Applications(VBA)中使用类?

我认为对于大多数支持OOP的语言来说,加速开发和减少引入错误是一个共同的好处。但是对于VBA,是否有特定标准?

解决方案

我看不出为什么VBA的标准与另一种语言会有所不同,尤其是在我们指的是VB.NET时。

我不会说有一个特定的标准,但是我从来没有真正找到过在VBA代码中使用类的有用地方。在我看来,它与Office应用程序周围的现有模型紧密相关,以至于在该对象模型之外添加其他抽象只会使事情变得混乱。

这并不是说无法在VBA中为类找到有用的地方,或者使用类不能做完全有用的事情,只是我从未发现它们在那种环境中有用。

我们也可以在不使用实际类的情况下重用VBA代码。例如,如果我们有一个被调用的VBACode。我们可以使用以下语法访问任何模块中的任何函数或者子:

VBCode.mysub(param1, param2)

如果创建对模板/文档的引用(就像对dll一样),则可以以相同的方式引用其他项目中的代码。

我认为标准与其他语言相同

如果我们需要将多个数据和某些方法结合在一起,并且还专门处理创建/终止对象时发生的情况,则类是理想的选择

假设如果我们有一些打开表单时会触发的过程,而其中一个过程花费的时间很长,那么我们可能会决定要为每个阶段计时。

我们可以创建一个带有用于启动和停止的明显函数的方法的秒表类,然后可以添加一个函数以检索到目前为止的时间,并使用表示正在计时的进程名称的参数在文本文件中报告该时间。我们可以编写逻辑以仅记录最慢的性能以进行调查。

然后,我们可以添加带有方法的进度条对象,以打开和关闭该对象以及显示当前操作的名称,以及基于以前存储的报告的时间(以毫秒为单位)和可能的剩余时间等

另一个示例可能是,如果我们不喜欢Access的用户组垃圾,则可以使用登录和注销方法以及组级用户访问控制/审核/记录某些操作/跟踪错误等功能创建自己的User类。

当然,我们可以使用一组不相关的方法和大量的变量传递来做到这一点,但是对我来说,将它们全部封装在一个类中似乎更好。

我们迟早会接近VBA的极限,但是它是一种非常强大的语言,如果公司将它与VBA绑定在一起,则实际上可以从中获得一些好的,复杂的解决方案。

通常,使用面向对象编程来开发软件,甚至使用Microsoft Access,也是一种好的做法。通过允许对象松散耦合,它将在将来实现可伸缩性,同时具有许多优点。从根本上讲,这意味着系统中的对象之间的依赖程度将降低,因此重构变得更加容易。我们可以通过使用类模块进行访问来实现。缺点是我们不能在VBA中执行类继承或者多态。最后,关于使用类,没有硬性规定,只有最佳实践。但是请记住,随着应用程序的增长,使用类进行维护变得更加容易。

这取决于谁来开发和维护代码。典型的"超级用户"宏编写者会通过使用类来混淆小型的即席应用程序。但是对于认真的开发,使用类的原因与其他语言相同。我们具有与VB6无继承相同的限制,但可以通过使用接口来实现多态。

类的一种好用法是表示实体和实体的集合。例如,我经常看到VBA代码将Excel范围复制到二维数组中,然后使用如下代码处理二维数组:

Total = 0
For i = 0 To NumRows-1
    Total = Total + (OrderArray(i,1) * OrderArray(i,3))
Next i

将范围复制到具有适当命名的属性的对象的集合中更容易理解,例如:

Total = 0
For Each objOrder in colOrders
    Total = Total + objOrder.Quantity * objOrder.Price
Next i

另一个示例是使用类来实现RAII设计模式(适用于Google)。例如,我可能需要做的一件事是取消保护工作表,进行一些操作,然后再次保护它。使用类可确保即使在代码中发生错误,工作表也将始终受到再次保护:

--- WorksheetProtector class module ---

Private m_objWorksheet As Worksheet
Private m_sPassword As String

Public Sub Unprotect(Worksheet As Worksheet, Password As String)
    ' Nothing to do if we didn't define a password for the worksheet
    If Len(Password) = 0 Then Exit Sub

    ' If the worksheet is already unprotected, nothing to do
    If Not Worksheet.ProtectContents Then Exit Sub

    ' Unprotect the worksheet
    Worksheet.Unprotect Password

    ' Remember the worksheet and password so we can protect again
    Set m_objWorksheet = Worksheet
    m_sPassword = Password
End Sub

Public Sub Protect()
    ' Protects the worksheet with the same password used to unprotect it
    If m_objWorksheet Is Nothing Then Exit Sub
    If Len(m_sPassword) = 0 Then Exit Sub

    ' If the worksheet is already protected, nothing to do
    If m_objWorksheet.ProtectContents Then Exit Sub

    m_objWorksheet.Protect m_sPassword
    Set m_objWorksheet = Nothing
    m_sPassword = ""
End Sub

Private Sub Class_Terminate()
    ' Reprotect the worksheet when this object goes out of scope
    On Error Resume Next
    Protect
End Sub

然后,我们可以使用它来简化代码:

Public Sub DoSomething()
   Dim objWorksheetProtector as WorksheetProtector
   Set objWorksheetProtector = New WorksheetProtector
   objWorksheetProtector.Unprotect myWorksheet, myPassword

   ... manipulate myWorksheet - may raise an error

End Sub

当此Sub退出时,objWorksheetProtector超出范围,工作表再次受到保护。

对于数据递归(也称为BOM表处理),自定义类非常有帮助,我认为有时必不可少。我们可以在没有类模块的情况下创建递归函数,但是许多数据问题无法有效解决。

(我不知道为什么人们不为VBA兜售BOM库集。也许XML工具有所作为。)

多个表单实例是类的常见应用程序(许多自动化问题否则无法解决),我假设问题与自定义类有关。

当我需要做某事时,我会使用类,而一个类会做得最好:)例如,如果我们需要响应(或者拦截)事件,则需要一个类。有些人讨厌UDT(用户定义的类型),但我喜欢它们,因此如果我想要纯英语的自我记录代码,则可以使用它们。 Pharmacy.NCPDP比strPhrmNum更容易阅读:)但是UDT受到限制,所以说我希望能够设置Pharmacy.NCPDP并填充所有其他属性。而且我也想做到这一点,以便我们不会意外更改数据。然后,我需要一个类,因为我们在UDT等中没有只读属性。

另一个考虑因素只是简单的可读性。如果我们要处理复杂的数据结构,那么知道只需要调用Company.Owner.Phone.AreaCode然后尝试跟踪所有内容的结构通常是有益的。特别是对于那些在我们离开两年后必须维护该代码库的人:)

我自己的两分钱是"有目的的代码"。不要无缘无故地使用课程。但是,如果我们有理由,请这样做:)