Excel精英培训网

 找回密码
 注册
数据透视表40+个常用小技巧,让你一次学会!
查看: 5320|回复: 3

[分享] VBA类模块初步(转)

[复制链接]
发表于 2015-10-15 14:58 | 显示全部楼层 |阅读模式
本帖最后由 爱疯 于 2015-10-15 15:02 编辑

这里简单地介绍VBA中的类模块,使大家能够在应用程序中创建并使用简单的类。
类是对象的“模板”。对象可以是任何事物,而类不会做任何事情,也不会占用内存,只有当类成为对象并使用Set语句和New关键字实例化为具体对象后,才能做事情并占用内存。实例化类为具体对象的语法为:
Dim C As Class1
Set C=New Class1

上述语句创建了一个名为C的对象,该对象的数据类型为定义的类Class1。
在详细介绍类之前,让我们先看看VBA的用户自定义数据类型,即使用Type关键字定义的变量。例如,下面的Type变量定义了雇员的信息:
Type Employee
    Name As String
    Address As String
    Salary As Double
End Type

上面的语句定义了变量Employee,包含元素Name、Address和Salary。接着,您可以声明一个Employee型的变量,并为其中的每个元素赋值:
Sub test()
    Dim Fan As Employee
    Fan.Name = "fanjy"
    Fan.Address = "YiChang"
    Fan.Salary = 1000
End Sub

用户自定义类型是很有用的,但是有三个主要的局限:
1、在编译时必须声明所有的自定义类型变量。虽然可以使用动态数组来处理多个自定义类型,但必须使用Redim Preserve关键词。并且,不能在运行时添加新的自定义类型变量。
2、不能控制赋给自定义类型中元素的值。例如,在上述代码中,有可能给Salary元素赋一个负值。
3、自定义类型不做任何事情,只是静态地存储数据。

用户自定义类型被广泛用于在对Windows API函数调用时,除此之外,使用类模块是更好的选择。类克服了用户自定义类型的局限。
1、使用New关键字,可以创建任意数量的类的新实例,并且能够将其存储在Collection对象中。
2、使用Property Let/Set/Get语句,可以编写代码验证赋给类元素的值,并且可以编写当值改变时执行的相应代码。例如,能够编写代码确保Salary的值不为负值。
3、类可以定义方法(使用Sub过程和Function过程),执行某项动作。

下面,让我们将自定义类型Employee转换为类。
首先,在VBE编辑器中插入一个类模块,并将其重命名为CEmployee,如图1所示。(类模块由属性和方法组成,类本身类似于名词;属性可以当作形容词,用来描述类;方法则为动词,执行操作。)
CEmployee类应该有三个属性:Name、Address和Salary。在类模块的声明部分声明三个Private变量来存储这些值,属性的实际值被存储在这三个私有变量中。
Private pName As String
Private pAddress As String
Private pSalary As Double
因为这些变量都被声明为私有的,因此仅能在类内部访问,在类的外部是不可见的。如何为这些变量赋值呢?这就是Property Let语句要做的工作。每个私有变量都有相应的Property Let语句。
Property Let Name(S As String)
    pName = S
End Property

Property Let Address(S As String)
    pAddress = S
End Property

Property Let Salary(D As Double)
    pSalary = D
End Property

Property Let语句用于给属性赋值,即将值引入类。在上例中,简单地将引入的值赋给私有变量。由于Property Let语句中能够包含代码,因此能够编写用于数据验证的代码。例如,改写Property Let Salary函数中的代码,使之不允许接受负值:
Property Let Salary(D As Double)
    If D > 0 Then
        pSalary = D
    Else
        '错误提示
        MsgBox "薪水怎能为负呢?"
    End If
End Property

与Property Let函数相对应的是Property Get函数,用于从类中获取属性的值。在CEmployee类中,相应的Property Get语句为:
Property Get Name() As String
    Name = pName
End Property

Property Get Address() As String
    Address = pAddress
End Property

Property Get Salary() As Double
    Salary = pSalary
End Property

Let语句和Get语句的数据类型必须相匹配。例如,Let Salary接受Double类型的值作为其参数,这意味着其返回的相应的Get属性的值必须是Double。如果数据类型不一致,就会导致编译错误。
这些Get语句简单地将三个属性向外公开。要创建只读属性,则忽略Property Let语句而仅使用Property Get语句。例如,WithholdingTax属性是只读的,在Get语句中的代码计算合适的值并将其公开,但避免从外部改变该属性的值:
Property Get WithholdingTax() As Double
    WithholdingTax = some_tax_calculation
End Property

该属性没有对应的Let语句,因此该属性只读,没有办法将值赋给WithholdingTax。
类模块能够包含方法,例如CEmployee类有一个用于雇员复核薪水的方法:
Public Sub PrintPaycheck()
    '放置打印复核的实际代码
End Sub

好了,我们已经完成了CEmployee类的初步定义,如图1所示。

图1:CEmployee类
下面,在标准模块的代码中实例化类,并使用其属性和方法。首先,在VBE编辑器中插入一个模块,声明一个CEmployee类型的变量。(注意,在复杂的应用程序中,在类模块中声明并实例化另一个类是完全合法的)
Dim Emp As CEmployee
接下来,实例化该类,创建一个可用来工作的实际对象,如下列语句:
Set Emp = New CEmployee
上述语句创建了一个名为Emp的对象,能够使用CEmployee类中的属性来引用特定的雇员信息,例如:
Emp.Name = “fanjy”
Emp.Address = “YiChang”
Emp.Salary = 1000
这三个语句调用在CEmployee类中声明的Property Let语句来赋值给类中的私有变量。我们能够使用下列代码读取对象的属性的值:
Debug.Print Emp.Name
Debug.Print Emp.Address
Debug.Print Emp.Salary
上述语句调用类模块中的Property Get语句并获取数据。我们也能读取类中的只读属性WithholdingTax:
Debug.Print Emp.WithholdingTax
由于WithholdingTax属性没有相应的Let语句,因此不能给该属性赋值。如果试图赋值:
Emp.WithholdingTax = 4000
则会导致:“编译错误:不能给只读属性赋值”。
也能够调用方法来执行操作:
Emp.PrintPaycheck
在集合中存储类的多个实例
如果到运行时还不知道有多少个雇员,那么能够在运行时按需要创建多个Emp对象,每创建一个Emp对象之后将其存储在集合中。例如:
Dim Coll As New Collection
Dim Ndx As Long
Dim Emp As CEmployee

For Ndx = 1 To NumberOfEmployees
    Set Emp = New CEmployee
    '设置Emp对象的属性
    Coll.Add Item:=Emp, Key:=Emp.Name
Next Ndx

上面的For…Next循环将创建CEmployee类的NumberOfEmployees个实例,具体数量由运行时决定,并将其存储在名为Coll的集合对象中。之后,能够使用For Each循环从Coll集合中获取每个雇员信息或执行操作:
For Each Emp In Coll
    Emp.PrintPaycheck
Next Emp
类的Instancing属性
类的Instancing属性决定其可见性(或称作作用域),默认属性值为1-Private,意味着类仅能在包含该类的工程中创建和访问。其他工程不能基于该类创建对象。对于绝大多数应用程序来说,Private是足够了。
Instancing属性的另一个值是2-PublicNotCreatable,表明其他工程能够将变量声明为该类,但是不能使用Set语句创建该类的实例。


在多个工程间使用类
如果一个工作簿需要使用定义在另一个工作簿中的类,则需要在包含类模块的工作簿中编写代码导出类到另一个工程。该类的Instancing属性必须是2-PublicNotCreatable。
假设Book1.xls工作簿中包含一个名为Class1的类模块,Book2.xls需要使用该类。首先,将Book1工作簿的工程名称从缺省的 “VBAProject”修改为唯一的名称,例如MyProject。然后,在VBE编辑器中激活Book2工作簿的界面,设置对Book1工作簿的引用,即在VBE中选择“工具——引用”,然后在列表中选择“MyProject”。然后,在Book2中,创建如下声明:
Public C As MyProject.Class1

因为Instancing的属性值为PublicNotCreatable,所以可以声明一个Class1类的变量,但不能创建该类的实例。因此,需要在Book1中编写一个函数来创建Class1的新实例,并返回该实例给Book2。在Book1中,创建下面的过程:

Public Function GetClass1() As Class1
    Set GetClass1 = New Class1
End Function

然后,在Book2中设置公共变量C为上述函数的结果,例如:
Set C=MyProject.GetClass1()
现在,C被设置为Class1的新实例。

好了,上面只是对类模块的一些初步介绍,还有一些知识未讲述,例如Property Set语句,留待以后在慢慢整理。

发表于 2015-10-15 15:46 | 显示全部楼层
回复

使用道具 举报

发表于 2016-11-23 18:35 | 显示全部楼层
回复

使用道具 举报

发表于 2021-9-18 14:40 | 显示全部楼层
很清楚,讲解得很好!!
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

小黑屋|手机版|Archiver|Excel精英培训 ( 豫ICP备11015029号 )

GMT+8, 2024-5-16 16:49 , Processed in 0.288008 second(s), 7 queries , Gzip On, Yac On.

Powered by Discuz! X3.4

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表