题
何时在Visual Basic for Applications(VBA)中使用类?
我假设加速开发并减少引入错误是大多数支持OOP的语言都有共同的好处。但是对于VBA,是否有具体的标准?
解决方案
这取决于谁将开发和维护代码。典型的“高级用户”通过使用类可能会混淆宏编写器黑客小型ad-hoc应用程序。但是对于认真的开发,使用类的原因与其他语言相同。你有与VB6相同的限制 - 没有继承 - 但你可以通过使用接口来实现多态性。
类的良好用途是表示实体和实体集合。例如,我经常看到将Excel范围复制到二维数组中的VBA代码,然后使用以下代码操作二维数组:
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 for it)。例如,我可能需要做的一件事是取消保护工作表,进行一些操作,然后再次保护它。使用类可确保即使代码中出现错误,也将始终再次保护工作表:
--- 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超出范围,工作表再次受到保护。
其他提示
我认为标准与其他语言相同
如果您需要将几个数据和一些方法联系在一起,并且还专门处理创建/终止对象时发生的情况,那么类是理想的
如果您在打开表单时有一些程序可能会触发,其中一个程序需要很长时间,您可能会决定要为每个阶段计时.......
您可以创建一个秒表类,其中包含用于启动和停止的明显函数的方法,然后您可以添加一个函数来检索到目前为止的时间并使用表示进程名称的参数在文本文件中报告它。定时。您可以编写逻辑来仅记录最慢的性能以进行调查。
然后,您可以添加一个进度条对象,其中包含打开和关闭它的方法,并显示当前操作的名称,以及以毫秒为单位的时间和基于先前存储的报告等的可能剩余时间等
另一个例子可能是,如果您不喜欢Access的用户组垃圾,您可以创建自己的User类,其中包含用于登录和注销的方法以及用于组级用户访问控制/审核/记录某些操作/跟踪错误等的功能
当然你可以使用一组不相关的方法和大量的变量传递来做到这一点,但是将它全部封装在一个类中对我来说似乎更好。
你迟早会接近VBA的极限,但它是一种非常强大的语言,如果你的公司与你联系,你实际上可以从中获得一些好的,复杂的解决方案。
在处理更复杂的API函数时,特别是在需要数据结构时,类非常有用。
例如,GetOpenFileName()和GetSaveFileName()函数采用具有许多成员的OPENFILENAME结构。你可能不需要利用所有这些,但它们在那里,应该被初始化。
我喜欢将结构(UDT)和API函数声明包装到CfileDialog类中。 Class_Initialize事件设置结构成员的默认值,这样当我使用该类时,我只需要设置我想要更改的成员(通过Property过程)。标志常量实现为枚举。因此,例如,要选择要打开的电子表格,我的代码可能如下所示:
Dim strFileName As String
Dim dlgXLS As New CFileDialog
With dlgXLS
.Title = "Choose a Spreadsheet"
.Filter = "Excel (*.xls)|*.xls|All Files (*.*)|*.*"
.Flags = ofnFileMustExist OR ofnExplorer
If OpenFileDialog() Then
strFileName = .FileName
End If
End With
Set dlgXLS = Nothing
该类将默认目录设置为My Documents,但如果我想,我可以使用InitDir属性更改它。
这只是一个类如何在VBA应用程序中非常有用的一个例子。
我不会说有一个特定的标准,但我从来没有真正找到一个在VBA代码中使用Classes的有用位置。在我看来,它与Office应用程序周围的现有模型紧密相关,在该对象模型之外添加额外的抽象只会让事情变得混乱。
这并不是说一个无法为VBA中的某个类找到一个有用的地方,或者使用一个类完成有用的东西,只是因为我从未发现它们在该环境中有用。
您也可以在不使用实际类的情况下重用VBA代码。例如,如果您有一个被调用的VBACode。您可以使用以下语法访问任何模块中的任何函数或子函数:
VBCode.mysub(param1, param2)
如果您创建对模板/ doc的引用(就像您的dll一样),您可以以相同的方式引用其他项目中的代码。
开发软件,即使使用Microsoft Access,使用面向对象编程通常也是一种很好的做法。通过允许对象松散耦合以及许多优点,它将在未来允许可扩展性。这基本上意味着系统中的对象将更少依赖于彼此,因此重构变得更加容易。您可以使用类模块实现此功能。缺点是您无法在VBA中执行类继承或多态。最后,没有关于使用类的硬性规则,只有最佳实践。但请记住,随着应用程序的增长,维护类的使用就越容易。
对于数据递归(a.k.a. BOM处理),自定义类非常有用,我认为有时是不可或缺的。您可以在没有类模块的情况下创建递归函数,但无法有效解决大量数据问题。
(我不知道为什么人们不会为VBA推销BOM库集。也许XML工具已经有所作为。)
多个表单实例是类的常见应用程序(许多自动化问题无法解决),我假设问题是关于自定义类。
当我需要做某事时我会使用类,并且类会做得最好:)例如,如果你需要响应(或拦截)事件,那么你需要一个类。有些人讨厌UDT(用户定义的类型),但我喜欢它们,所以如果我想要简单的英文自我记录代码,我会使用它们。 Pharmacy.NCPDP比strPhrmNum更容易阅读:)但UDT是有限的,所以说我希望能够设置Pharmacy.NCPDP并填充所有其他属性。而且我也希望它能让你不小心改变数据。然后我需要一个类,因为你在UDT中没有readonly属性等等。
另一个考虑因素是简单的可读性。如果您正在进行复杂的数据结构,那么知道您只需要调用Company.Owner.Phone.AreaCode然后尝试跟踪所有结构的位置通常是有益的。特别是对于那些在你离开后2年必须维护代码库的人:)
我自己的两分钱是“有目的的代码”。不要没有理由使用课程。但如果你有理由,那就去做吧。)
如果我想创建一个自封装的代码包,我将使用类,我将在许多VBA项目中使用这些代码,这些代码会遇到各种客户端。
您可以在access中定义一个比recordsets和querydef更方便的sql包装器类。例如,如果要根据另一个相关表中的条件更新表,则不能使用连接。您可以构建一个vba recorset和querydef来做到这一点但是我发现一个类更容易。此外,您的应用程序可能有一些需要更多2个表的概念,为此使用类可能更好。例如。您应用程序跟踪事件。事件有几个属性将保存在几个表{用户及其联系人或配置文件,事件描述;状态跟踪;检查表帮助支持人员回复事件;回复...}。为了跟踪所涉及的所有查询和关系,oop可能会有所帮助。能够使用Incident.Update(xxx)代替所有编码......这是一种解脱。
我不明白为什么VBA的标准与另一种语言有什么不同,特别是如果你指的是VB.NET。