在VBA中使用Classes有什么好处?

时间:2010-01-29 11:48:27

标签: excel vba excel-vba class

我正在Excel 2007中进行一些VBA编程,并且有一个工作簿将所有数据表从中复制到另一个工作表中。新表将有几个标题行,我想跟踪它们的位置,所以我不必经常在其中找到单词。

在Excel文档打开时,使用类并使它们保持运行是最简单的吗?或者这会使它变得沉重和难以处理,我应该继续使用子程序?使用类有什么好处?这不像我有几个对象,只有工作表和列上的验证。

3 个答案:

答案 0 :(得分:75)

使用类而不仅仅是子例程的优点是类创建了一个抽象级别,允许您编写更清晰的代码。不可否认,如果您以前从未在VBA中使用过课程,那么学习曲线就会存在,但我相信它确实值得花时间来解决。

如果您不断向函数和子程序添加参数,则应切换到类的一个关键指示。在这种情况下,几乎总是最好使用类。

我复制了one of my previous Stack Overflow answers中的课程解释:


这是一个很长的例子,说明如何使用类可以帮助你。虽然这个例子很冗长,但它将向您展示面向对象编程的一些原则如何真正帮助您清理代码。

在VBA编辑器中,转到Insert > Class Module。在“属性”窗口(默认情况下在屏幕的左下角)中,将模块的名称更改为WorkLogItem。将以下代码添加到类中:

Option Explicit

Private pTaskID As Long
Private pPersonName As String
Private pHoursWorked As Double

Public Property Get TaskID() As Long
    TaskID = pTaskID
End Property

Public Property Let TaskID(lTaskID As Long)
    pTaskID = lTaskID
End Property

Public Property Get PersonName() As String
    PersonName = pPersonName
End Property

Public Property Let PersonName(lPersonName As String)
    pPersonName = lPersonName
End Property

Public Property Get HoursWorked() As Double
    HoursWorked = pHoursWorked
End Property

Public Property Let HoursWorked(lHoursWorked As Double)
    pHoursWorked = lHoursWorked
End Property

上面的代码将为我们提供一个强类型对象,该对象特定于我们正在使用的数据。当您使用多维数组来存储数据时,您的代码类似于:arr(1,1)是ID,arr(1,2)是PersonName,arr(1,3)是HoursWorked。使用该语法,很难知道什么是什么。假设您仍然将对象加载到数组中,而是使用我们在上面创建的WorkLogItem。这个名字,您可以arr(1).PersonName来获取此人的姓名。这使您的代码更容易阅读。

让我们继续这个例子。我们将尝试使用collection

,而不是将对象存储在数组中

接下来,添加一个新的类模块并将其命名为ProcessWorkLog。将以下代码放在那里:

Option Explicit

Private pWorkLogItems As Collection

Public Property Get WorkLogItems() As Collection
    Set WorkLogItems = pWorkLogItems
End Property

Public Property Set WorkLogItems(lWorkLogItem As Collection)
    Set pWorkLogItems = lWorkLogItem
End Property

Function GetHoursWorked(strPersonName As String) As Double
    On Error GoTo Handle_Errors
    Dim wli As WorkLogItem
    Dim doubleTotal As Double
    doubleTotal = 0
    For Each wli In WorkLogItems
        If strPersonName = wli.PersonName Then
            doubleTotal = doubleTotal + wli.HoursWorked
        End If
    Next wli

Exit_Here:
    GetHoursWorked = doubleTotal
        Exit Function

Handle_Errors:
        'You will probably want to catch the error that will '
        'occur if WorkLogItems has not been set '
        Resume Exit_Here


End Function

上述课程将用于与WorkLogItem的集体“做某事”。最初,我们只是将其设置为计算总工作小时数。让我们测试一下我们编写的代码。创建一个新模块(这次不是类模块;只是一个“常规”模块)。将以下代码粘贴到模块中:

Option Explicit

Function PopulateArray() As Collection
    Dim clnWlis As Collection
    Dim wli As WorkLogItem
    'Put some data in the collection'
    Set clnWlis = New Collection

    Set wli = New WorkLogItem
    wli.TaskID = 1
    wli.PersonName = "Fred"
    wli.HoursWorked = 4.5
    clnWlis.Add wli

    Set wli = New WorkLogItem
    wli.TaskID = 2
    wli.PersonName = "Sally"
    wli.HoursWorked = 3
    clnWlis.Add wli

    Set wli = New WorkLogItem
    wli.TaskID = 3
    wli.PersonName = "Fred"
    wli.HoursWorked = 2.5
    clnWlis.Add wli

    Set PopulateArray = clnWlis
End Function

Sub TestGetHoursWorked()
    Dim pwl As ProcessWorkLog
    Dim arrWli() As WorkLogItem
    Set pwl = New ProcessWorkLog
    Set pwl.WorkLogItems = PopulateArray()
    Debug.Print pwl.GetHoursWorked("Fred")

End Sub

在上面的代码中,PopulateArray()只创建了WorkLogItem的集合。在您的实际代码中,您可以创建类来解析Excel工作表或数据对象以填充集合或数组。

TestGetHoursWorked()代码简单地演示了如何使用类。您注意到ProcessWorkLog被实例化为对象。实例化后,WorkLogItem的集合将成为pwl对象的一部分。你在行Set pwl.WorkLogItems = PopulateArray()中注意到了这一点。接下来,我们简单地调用我们编写的函数,该函数作用于集合WorkLogItems

为什么这有用?

假设您的数据发生了变化,并且您想要添加新方法。假设您的WorkLogItem现在包含HoursOnBreak的字段,并且您想添加一种新方法来计算它。

您需要做的就是向WorkLogItem添加一个属性,如下所示:

Private pHoursOnBreak As Double

Public Property Get HoursOnBreak() As Double
    HoursOnBreak = pHoursOnBreak
End Property

Public Property Let HoursOnBreak(lHoursOnBreak As Double)
    pHoursOnBreak = lHoursOnBreak
End Property

当然,您需要更改填充集合的方法(我使用的示例方法是PopulateArray(),但您可能应该有一个单独的类)。然后,您只需将新方法添加到ProcessWorkLog课程中:

Function GetHoursOnBreak(strPersonName As String) As Double
     'Code to get hours on break
End Function

现在,如果我们想要更新我们的TestGetHoursWorked()方法以返回GetHoursOnBreak的结果,我们只需要添加以下行:

    Debug.Print pwl.GetHoursOnBreak("Fred")

如果传入了表示数据的值数组,则必须在代码中找到使用数组的每个位置,然后相应地更新它。如果您使用类(及其实例化对象),则可以更轻松地更新代码以使用更改。此外,当您允许以多种方式使用类时(可能一个函数只需要4个对象属性而另一个函数需要6个),它们仍然可以引用同一个对象。这使您无法为不同类型的函数使用多个数组。

如需进一步阅读,我高度建议您获取VBA Developer's Handbook, 2nd edition的副本。这本书充满了很好的例子和最佳实践以及大量的示例代码。如果你为VBA投入大量时间进行一个认真的项目,那么值得花些时间来研究这本书。

答案 1 :(得分:6)

如果有很多子例程或子例程很长,那么将代码构造到类中可能会有所帮助。如果只有几个子程序,比如每个只有10行代码,那么这就是过度杀戮。将代码构造到类中的好处是,当您回到它的后面时,它更容易阅读和更改。因此,将代码结构化为类的另一个原因是代码可能需要更改行

答案 2 :(得分:4)

还有一件事可以补充其他贡献者所说的优点(对不起,如果它在Ben McCormack的某个地方有很好的答案而我错过了它)。如果您的VBA脚本可能在某些时候被重新编程,则可以使用类。

例如,我正在设计一种订单管理系统。它将被一些同事使用很长一段时间,但如果订购规则发生变化,可能需要重新编程。因此,我设计了一个基本库存项目类,它收集有关库存项目的所有信息。然而,关于如何针对任何订单分析该数据的规则是在易于访问且评论良好的子例程中编写的。通过这样做,我希望未来的VBA程序员可以轻松地更改生成订单的数学规则,而不必处理有关特定库存项目的所有数据的收集方式(这完全由类中的子程序和函数完成) ,当班级交给股票号码时激活。一个班级'公共财产也被智能感知所吸引,让下一个程序员和你自己也可以更轻松地完成它。

我想关键是,类可以通过这种方式让后来的用户更容易生活如果他们编码一些基本的信息集,或者某些概念对象,它总是可能与该计划的使用背景。