VBA 类模块学习

您所在的位置:网站首页 模块对象是 VBA 类模块学习

VBA 类模块学习

2024-07-01 07:26| 来源: 网络整理| 查看: 265

VBA 类模块学习 1  问题背景2  创建自己的对象3  属性过程4  创建集合4.1  `Collection` 对象创建集合4.2   在类模块中创建集合 5  封装 本文中,大部分内容源于书籍《Excel 2007 VBA 参考大全》,ISBN:9787115311696。感谢原书第一作者及译者。

工作表模块、图表工作表模块、工作簿模块和用户窗体模块都是类模块。不过,这些模块都是特定类型的类模块,其行为与自己创建的类模块稍有不同。 这些特定的模块专门设计来支持与它们相关联的对象,提供对该对象的事件过程的访问,并且如果不删除与之相关的对象,就不能删除相应的对象模块。

1  问题背景

创建一个 Employee 对象。希望在该对象中存储雇员的姓名、每周工作时间和薪水等级,然后利用这些信息计算雇员每周的薪水。 可创建带有3个属性和1个方法的Employee对象,属性用来包含必需的数据,方法用来计算每周的薪水。

2  创建自己的对象

在类模块中:

公共变量 都表现为类对象的 属性公共函数 或 子过程 都表现为类对象的方法。函数是能产生返回值的方法,而子过程是没有返回值的方法。

创建一个名为 CEmployee 的类模块。声明了3个公共变量(属性):Name、HoursPerWeek 和 Rate,一个公共函数(方法) WeeklyPay。

Public Name As String Public HoursPerWeek As Double Public Rate As Double Public Function WeeklyPay() As Double WeeklyPay = HoursPerWeek * Rate End Function

创建一个名为 modExamples 的标准模块,插入如下代码:

Sub EmployeePay() Dim clsEmployee As CEmployee Set clsEmployee = New CEmployee clsEmployee.Name = "Mary" clsEmployee.Rate = 15 clsEmployee.HoursPerWeek = 35 MsgBox clsEmployee.Name & "每周可挣得$" & clsEmployee.WeeklyPay & "/wk" End Sub 代码从 类模块 CEmployee 中生成一个 Employee 对象。该模块声明 变量 clsEmployee 为 CEmployee 类型。Set 语句 将 CEmployee 的一个新实例赋给 变量 clsEmployee 。也就是说,Set语句创建了这个新对象。以上代码给对象的3个属性赋值,然后产生显示在所示的消息框中的消息。消息中使用了 Employee 对象的 Name 属性并执行了该对象的 WeeklyPay 方法。 在这里插入图片描述

当仅需要创建对象变量的 单个实例 时,设置标准代码模块的另一种方法如下:

Dim Employee As New CEmployee Sub EmployeePay() Employee.Name = "Mary" Employee.Rate = 15 Employee.HoursPerWeek = 35 MsgBox Employee.Name & "每周可挣得$" & Employee.WeeklyPay & " /wk" End Sub

这里,在声明行中使用关键字 New。本例中,当在代码里第一次引用 Employee 对象时,将自动创建该对象。

3  属性过程

如果通过 声明公共变量定义属性,则为可读/写属性,能直接访问并直接赋新值,正如在上一节中所看到的。

如果需要在属性中执行检查或计算,那么应在类模块中使用 Property Let 过程和 Property Get 过程定义属性,而不是使用公共变量。

Property Get 过程允许类模块控制访问属性的方式Property Let 过程允许类模块控制给属性赋值的方式也可以使用 Property Set 过程,其作用与 Property Let 过程相似,但用于处理对象而不是值。

例如: 假设希望将雇员的 工作时间分为正常时间和加班时间,超过35小时都属于加班时间。 需要一个 HoursPerWeek 属性,包含正常时间和加班时间,可读取并可赋新值。 要使类模块将工作时间分成正常时间和加班时间,则应设置 NormalHours 和 OverTimeHours 属性,可读取但不可直接赋新值。此时,在 CEmployee 类模块 中设置的代码为:

Public Name As String Public Rate As Double Private dNormalHrs As Double Private dOverTimeHrs As Double Public Function WeeklyPay() As Double '返回每周的薪水 WeeklyPay = dNormalHrs * Rate + dOverTimeHrs * Rate * 1.5 End Function Property Let HoursPerWeek(dHours As Double) '将输入的工作时间转换为正常时间和加班时间 dNormalHrs = WorksheetFunction.Min(35, dHours) dOverTimeHrs = WorksheetFunction.Max(0, dHours - 35) End Property Property Get HoursPerWeek() As Double '返回每周总的工作时间 HoursPerWeek = dNormalHrs + dOverTimeHrs End Property Property Get NormalHours() As Double '返回正常工作时间 NormalHours = dNormalHrs End Property Property Get OverTimeHours() As Double '返回加班时间 OverTimeHours = dOverTimeHrs End Property

HoursPerWeek 不再作为变量在声明部分进行声明,而是添加了两个新的私有变量:dNormalHrs 和 dOverTimeHrs。

此时,通过 Property Let 过程定义 HoursPerWeek,用来处理赋值给 HoursPerWeek 属性时的输入值。该过程将工作时间分成正常时间和加班时间。当访问 HoursPerWeek 属性的值时,Property Get 过程为该属性返回正常时间和加班时间之和。

仅仅通过 Property Get 过程定义 NormalHours 和 OverTimeHours,分别返回私有变量dNormalHrs 和 dOverTimerHrs 的值。这使得 NormalHours 属性和 OverTimeHours 属性都是只读属性,除了通过 HoursPerWeek 属性外,没有办法给这两个属性赋值。

用更新的 WeeklyPay 函数来计算薪水,正常时间以标准薪水等级计算,加班时间以1.5倍的标准薪水等级计算。可以将标准模块的代码修改如下

Sub EmployeePay() Dim clsEmployee As CEmployee Set clsEmployee = New CEmployee '创建CEmployee对象的实例 clsEmployee.Name = "Mary" '定义属性 clsEmployee.Rate = 15 clsEmployee.HoursPerWeek = 45 '显示属性 MsgBox clsEmployee.Name & "每周可挣得$" _ & clsEmployee.WeeklyPay & "/wk" _ & ",包括" & clsEmployee.OverTimeHours _ & "小时的加班时间" End Sub 4  创建集合 4.1  Collection 对象创建集合

此时,已经有了一个 Employee 对象,如果希望有许多 Employee 对象,那么除了在集合里组织这些对象外,还有更好的方法吗?VBA有一个 Collection 对象,可在 标准模块 中使用,如下面的代码所示:

Dim mcolEmployees As New Collection '包含Employee对象的集合 Sub AddEmployees() Dim clsEmployee As CEmployee Dim lCount As Long For lCount = 1 To mcolEmployees.Count '确保集合是空的 mcolEmployees.Remove 1 Next lCount Set clsEmployee = New CEmployee '定义Employee对象 clsEmployee.Name = "Mary" clsEmployee.Rate = 15 clsEmployee.HoursPerWeek = 45 mcolEmployees.Add clsEmployee, clsEmployee.Name '添加Employee对象到集合中 Set clsEmployee = New CEmployee '定义Employee对象 clsEmployee.Name = "Jack" clsEmployee.Rate = 14 clsEmployee.HoursPerWeek = 35 mcolEmployees.Add clsEmployee, clsEmployee.Name '添加Employee对象到集合中 MsgBox "雇员数=" & mcolEmployees.Count '显示集合中的数据 MsgBox "mcolEmployees(2).Name = " & mcolEmployees(2).Name MsgBox "mcolEmployees(""Jack"").Rate = " & mcolEmployees("Jack").Rate For Each clsEmployee In mcolEmployees '处理所有的雇员 MsgBox clsEmployee.Name & "每周挣得$" & clsEmployee.WeeklyPay Next clsEmployee End Sub

在标准模块的顶部,声明变量 mcolEmployees 为一个新集合。

AddEmployees 过程在 For…Next 循环内使用该集合的 Remove 方法 删除所有现有的对象。 语句 .Remove 1 始终删除集合中的第一个对象,因为只要删除了集合中的第一个对象,第二个对象会自动成为第一个,依此类推。 正常情况下,可省掉这个步骤,因为初始化后的集合为空。这里仅仅是为了演示 Remove 方法,同时也允许多次运行本过程而不必担心集合中的项目越来越多。

AddEmployees 过程创建第一个雇员 Mary,并使用该集合的 .Add 方法将 Mary 对象添加到集合中。 Add 方法的 第一个参数是对对象自身的引用;第二个参数为可选参数,是一个标识关键字,用于在后面引用该对象。 本例中,使用 Employee 对象的 Name 属性作为关键字。在该过程中用相同的方式创建 Jack 对象。

如果为集合中的每个成员都提供了一个关键值,则该值必须是唯一的。 当试图添加一个新成员到集合中,而其关键值与已经使用的成员的关键值相同时,会发生运行时错误。不建议使用人名作为关键值,因为不同的人可能会有相同的名字。建议使用一个唯一的标识符,例如社会保障号(Social Security number)。

MsgBox 语句说明,可采用与引用Excel内置集合相同的方式引用所创建的集合。 例如,Employees 集合有 Count 属性,能通过位置或关键值(如果已输入了关键值)引用集合成员。

4.2   在类模块中创建集合

同样可在类模块中创建集合,但这样有优点也有缺点。

优点是可更好地控制与集合的交互,可防止直接访问集合,而且代码被封装在单个的模块中,更易传输,也更易维护。缺点是需要采取更多的步骤创建集合,并失去了引用集合本身及其成员的一些快捷方式。

类模块 CEmployees 中的代码为:

Private mcolEmployees As New Collection '包含Employee实例的集合 Public Function Add(clsEmployee As CEmployee) '添加雇员到集合中的方法 mcolEmployees.Add clsEmployee, clsEmployee.Name End Function Public Property Get Count() As Long '返回Count属性 Count = mcolEmployees.Count End Property Public Property Get Items() As Collection '返回集合 Set Items = mcolEmployees End Property Public Property Get Item(vItem As Variant) As CEmployee '返回该集合的成员 Set Item = mcolEmployees(vItem) End Property Public Sub Remove(vItem As Variant) '删除该集合的成员 mcolEmployees.Remove vItem End Sub

当集合处于自己的类模块中时,在标准模块中不再能够直接使用集合的4个方法(Add、Count、Item 和 Remove),而需要在类模块中创建自己的方法和属性,即便不打算修改该集合的方法。

另一方面,可以完全控制是选择作为方法来执行,还是选择作为属性来修改。

在类模块 CEmployees 中,Function Add、Sub Remove、Property Get Item 和 Property Get Count 过程传递了该集合的方法的大多数功能。在 Property Get Items 过程中有一个新功能。Property Get Item 返回对集合中单个成员的引用,而 Property Get Items 返回对整个集合的引用,因而能在 For Each…Next 循环中使用。 此时,标准模块中的代码如下:

Sub AddEmployees() Dim clsEmployees As CEmployees Dim clsEmployee As CEmployee Dim lCount As Long Dim vNames, vRates, vHours Dim sText As String vNames = Array("Mary", "Jack", "Anne", "Harry") '输入数据 vRates = Array(15, 14, 20, 17) vHours = Array(45, 35, 40, 40) Set clsEmployees = New CEmployees '初始化集合 For lCount = LBound(vNames) To UBound(vNames) '定义和添加雇员到集合中 Set clsEmployee = New CEmployee clsEmployee.Name = vNames(lCount) clsEmployee.Rate = vRates(lCount) clsEmployee.HoursPerWeek = vHours(lCount) clsEmployees.Add clsEmployee Set clsEmployee = Nothing Next lCount MsgBox "雇员数=" & clsEmployees.Count '显示集合中的数据 MsgBox "Employees.Item(2).Name = " & clsEmployees.Items(2).Name MsgBox "Employees.Item(""Jack"").Rate = " & clsEmployees.Items("Jack").Rate For Each clsEmployee In clsEmployees.Items sText = sText & clsEmployee.Name & "挣得$" & clsEmployee.WeeklyPay & vbCrLf Next clsEmployee MsgBox sText End Sub

将变量 clsEmployees 声明为 CEmployees 类型,接下来的代码定义了3个数组,以便区分使用的数据。

初始化 Employees 集合后,创建 Employee 对象的实例并将它们添加到集合中。作为一处不大的便利,当使用 Employees 集合的 Add 方法时不再需要指定关键值,clsEmployees 中的 Add 方法完成了这个工作。

第二、第三和第四个MsgBox 语句显示了引用该集合及其成员所需的新属性,使用 Item 属性引用成员,使用 Items 属性引用整个集合。

5  封装

类模块允许封装代码和数据。在这种方式下,代码和数据的使用与共享都极为容易,维护也变得简单多了。 用户不必知道代码是如何工作的,只需要知道类模块代表的对象,以及与对象相关的属性和方法。当必须调用Windows API(应用程序接口)来执行正常的VBA代码不可能完成的任务时,这是特别有用的。示例将演示如何封装非常复杂的代码和创建非常有用的对象。

类模块提供了封装代码的一种机制,可以在其他工作簿中使用这些代码,或者与其他程序员共享这些代码,从而缩短开发时间。可以很容易地复制类模块到另一个工作簿中。在工程资源管理器窗口中,在工程之间能直接拖放类模块。

右键单击工程资源管理器中的模块并选择“导出文件”,可创建一个能够复制到另一台PC中的文本文件,从而将类模块中的代码导出到文件中。要把该文件再导入到另一个工作簿中,只需要右键单击工程资源管理器中另一个工作簿的工程并选择“导入文件”。 至此,本章已经从普通编程设计的角度分析了类模块。接下来介绍如何使用类模块更好地控制Excel。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3