大家都知道,VB是一种半面向对象(也有人称之为“伪面向对象”)的语言,他虽然可以写自定义的类,但是由于种种原因,使得他在这方面的发育产生了一点问题,比如说:VB写出来的类是不能继承的(不孕不育?!-_-#...传说VB的偶像是东方不败……残念-_-#!),你无法像在C++/C#/JAVA==中一样自由的使用类。你很难想象类没有继承会有啥用途。还有,在VB中,一个类模块只能写一个类,这意味着如果你的Project中使用了N个类,你必须忍受它带着N个类模块文件,甚至其中有些只是小小小类。这些都造成VB的类的可用性和重用性不佳。
但是,VB是否在类的方面就这么无能呢?答案是否定的,在很多时候(特别是需要将很多功能封装,形成模块化的时候),VB 的自定义类还是能派上用场的。这里,有人可能会想到使用ActiveX控件,虽然在VB中,可以使用ActiveX控件做到上述功能,但是写ActiveX控件要比写类麻烦得多,而且ActiveX控件的资源占用也要比类大。当然,如果你只想去往上搜搜ActiveX控件,然后拼成一个程序的话,我完全可以对你说:“原谅我浪费了你的生命!”
那么接下来,我们就已Animal为主题讲讲在VB6中写自定义类的方法~
成员函数和成员变量
首先,我们需要添加一个类模块,然后将它的名称改成Animal,那么,这个类的名字就是Animal。然后再写入下列代码:引用:
Private m_strName As String, m_bytLegs As Byte, m_blnBark As Boolean如此,我们已经写好了Animal类的大体结构,现在我们来看看这些代码。引用:
Public Sub SetAnimalName(ByVal strName As String)
m_strName = strName '/// 设置Animal的名字
End Sub
Public Function GetAnimalName() As String
GetAnimalName = m_strName '/// 返回Animal的名字
End Function
Public Sub SetLegs(ByVal bytLegs As Byte)
m_bytLegs = bytLegs '/// 设置Animal的腿数
End Sub
Public Function GetLegs() As Byte
GetLegs = m_bytLegs '/// 返回Animal的腿数
End Function
Public Sub SetBark(ByVal blnBark As Boolean)
m_blnBark = blnBark '/// 设置Animal是否会叫
End Sub
Public Function GetBark() As Boolean
GetBark = m_blnBark '/// 返回Animal是否会叫
End Function
Private m_strName As String, m_bytLegs As Byte, m_blnBark As Boolea '/// 声明成员变量这里的Private等同于Dim,为了更直观,所以这样写。因为在类模块中,也可以用Public声明变量。用Private(Dim)和Public声明的变量,他们的作用域不同:Private声明的变量只能在类的内部被访问,在外部是不可见的,而用Public声明的变量在外部是可见的,这意味着你在外部就可以直接修改类中的变量,这看起来很方便,但是任何无意的修改所造成的后果都可能是无法预估的。所以,为了更好的封装类,通常用Private声明。当然,在某些特定条件下,我们也可以使用Public。(详情请看下文)
然后,我们定义了几个成员函数,用它完成相应的功能。至于什么是成员函数(还有刚才提到的成员变量),这点大家可以不必了解,这其实是C++的叫法,只不过上校在VB中也习惯这么叫。这完全是称呼问题。大家可以看到,我把变量名写成m_XXX,这里的m_是成员变量的标记,用于和其他变量区分,这是典型的匈牙利命名法。
BTW:关于匈牙利命名法,大家可以去Google了解下。个人感觉匈牙利命名法在VB中实用性很强,所以我的声明习惯基本都符合匈牙利命名法。
然后我们看看后面的一些函数。他们都用Public声明,原因和前面声明成员变量类似,这里不再阐述。函数功能也都已注释,大家可以自己看。
类的使用方法
类写好了,我们要开始使用它。和使用其他数据类型一样,我们要先声明。但是有所不同的是,类的声明通常有两种方法:引用:
Dim cAnimal As Animal两者相同之处在于都声明了一个名为cAnimal的对象变量,并用New将其实例化,这是使用自定义类所必需的。而两者的不同在于后者在声明Animal对象变量的时候就将其实例化,而前者则是先声明对象变量,再在适当的时候将其实例化。相比之下,前者可以控制实例化类的时间和场合,因此更为自由,普遍。
Set cAnimal = New Animal
————————————————————
Dim cAnimal As New Animal
说完了声明我们先来讲讲类的销毁。每一个类的使用都要占用一定的资源,当不需要这些类时,需要释放他们所占用的资源,这是良好的Coding Habits~
类的销毁比较简单,只需要引用:
Set cAnimal = Nothing即可。
这里建议大家在声明完类之后,就马上在适当的地方写入销毁类的代码,避免因为遗忘而造成错误。
接下来,我们要开始使用我们的Animal类引用:
Private Sub Form_Load()我们在窗体加载的时候实例化了cAnimal这个类变量,,然后设置了Name/Legs/Bark等属性。
'/// 设置相关选项
Set cAnimal = New Animal
With cAnimal
.SetAnimalName ("IceBear")
.SetLegs (4)
.SetBark (True)
End With
End Sub
使用类实例和使用控件很像,当输入类名+. 即cAnimal.的时候,就会出现信息条
然后进行相应操作即可。引用:
Private Sub Form_Click()单击窗口后,立即窗口就会出现:引用:
With cAnimal
Debug.Print "Animal Name:" & .GetAnimalName & vbNewLine & "Animal Legs:" & .GetLegs
Debug.Print "Is Bark:" & .GetBark
End With
End Sub
Animal Name:IceBear这时,你就可以向你暗恋的女生说你能在VB里写类了。(但是千万别提到这类不能继承……-_-#!)
Animal Legs:4
Is Bark:True
类的属性
不知道大家有没有发现,无论我们是获取,还是设置Name/Legs/Bark的信息,都需要使用一个单独的成员函数。而我们在使用控件时,那些都是作为属性(Property)来实现的。所以,接下来,我们要研究如何在自定义类中实现属性。
为了方便之后对比,我们仅把Name转化为属性实现:引用:
Public Property Get Name() As String我们可以发现,属性的定义和成员函数定义很相似。Get就像Function,而Let类似于Sub。因为Get是返回属性,所以通常没有参数,而Let为了让我们设置属性,所以必须要有一个参数。
Name = m_strName '/// 返回Animal的名字
End Property
Public Property Let Name(ByVal strAnimalName As String)
m_strName = strAnimalName '/// 设置Animal的名字
End Property
这样,我们就可以像在控件中一样,直接使用Name这个属性来设置和获取相关信息。
BTW:如果你觉得编写属性过程太麻烦,可以使用工具菜单中的添加过程来减轻工作量
另外,我们可以通过只编写Let或者Get过程来生成只读/只写的属性
看到这里,不知道大家想过没有,既然Get/Let属性过程很像Function/Sub,而Function/Sub是可以有多个参数的,那么Get/Let属性是不是也可以有参数呢?
当然,这是可以的,只不过相当少见(我们平常在用控件属性时,MS都没遇到过),而且它还有着比较严格的规定和限制,如果你对这方面不感兴趣或者认为用不着,可以跳到后面去。
现在我们就额外添加一个Age的参数,看看如何在属性过程中使用多参数。
首先我们的属性过程要改为:引用:
Public Property Get Name(ByVal intAge As Integer) As StringGet和Let同时多了一个相同的参数。不管怎么样,Let过程总会比Get多一个参数。并且,Get过程的1--N个参数要和Let过程的1--N个参数一致(包括函数名、类型、传入方式等),Get的返回值类型要和Let的第N+1 个参数(最后一个参数)的类型相等。
Name = m_strName '/// 返回Animal的名字
End Property
Public Property Let Name(ByVal intAge As Integer, ByVal strAnimalName As String)
m_strName = strAnimalName & " is " & intAge
End Property
我们可以通过一张图片来大致了解下Let过程的调用。
所以,相应的,我们对Name属性的代码也要更改如下:引用:
.Name(20) = "IceBear"运行后,立即窗口就会出现:引用:
& .Name(19)
Animal Name:IceBear is 20但是,当我们在使用Get过程时,那个参数并没有作用,却又必须存在。这时,我们可以将其声明为可选参数,这样,Get确有参数存在,我们也可以免去放入无用实参的麻烦。引用:
Animal Legs:4
Is Bark:True
Public Property Get Name(Optional ByVal intAge As Integer) As String虽然在属性过程中使用多个参数可以在一定程度上加快便捷,但是这是牺牲实用性为代价的。多个参数的过程属性在使用上非常不便,而且看起来不伦不类的。所以,上校建议如非真的必要,就不要使用多参数。
特殊的类属性
其实,在VB的Class里,还有一种很普通但不普遍的类属性——共有成员变量。
我们在之前强调过,一般情况下,成员变量应为私有,这是封装的要求。而如果成员变量的属性是共有的,则其在外部也可以被访问,这就形成了一个简单的类属性,属性名就是他的变量名。
和属性过程相比,公有成员变量不能设置为只读/只写,无法在更改自己的同时更改其他的属性值,也不能验证数据等。(因为修改共有变量属性仅是修改它的值,而通过属性过程,则可以进行一系列的操作)但是共有成员变量作为属性,能很大程度上减少代码量,简化操作。下面有一些使用类属性的原则,仅供参考:引用:
From:M$ MSDNBTW: 不要仅仅为了避免函数调用的额外开销而用公共变量来实现一个属性。其实,由于类型库的要求,Visual Basic 在类模块中以任意方式将公共变量作为属性过程对使用。
以下情况应使用属性过程:
• 属性为只读,或一旦设置就不能改变。
• 属性已设置的值需要验证。
• 超出特定范围的值。例如,负数,虽符合属性的数据类型,但属性如果允许这样的假设值出现,就会导致程序出错。
• 属性的设置可导致一些对象状态的明显变化,例如,Visible 属性。
• 属性设置可改变内部变量或其它属性的值。
以下场合应使用只读属性的公共变量:
• 属性是自验证类型。例如,如果一个不是 True 或 False 的值被赋给 Boolean 变量,则或者出错,或者数据自动转换。
• 在数据类型所支持的范围内的值都是有效的。象许多 Single 或 Double 类型的属性。
• 属性是 String 数据类型,并没有大小或字符串取值的限制。
属性和成员函数的选择
在类编写中,经常会遇到选择属性还是成员函数实现的问题,就此,我们对属性和成员函数的选择给出自己的判断标准(仅供参考):
一般来说,属性是对某个对象本身特征的描述,属性是对向所拥有的,比如Name、Age、Height等等;而成员函数则用于执行对象的某种行为/动作,比如Move、Jump、Cook等。(此处是对于VB来说,CPP MS没有类似于VB属性过程的东西,所以成员函数也用于设置属性。)
不过,有时,就像人类行为也存在一个边界不清一样,可能上述的方法不管用。那么,你可以根据自身的要求和条件或者说癖好决定。
这里,还有最后一招,这招是上校的独门绝技,不仅在此处适用,而且在任何学科,任何情况下皆适用。最大的好处在于你会无条件的服从自己~这招就是~~铛~~铛~~铛~~掷——硬——币。
回声:)让我送你去见毛主席吧~-_-#!
构造函数与析构函数
当我们希望类在被初始化时,自动的执行一些操作。比如:为某个成员变量赋初始值。这时候,我们就需要构造函数。当然,这里的构造函数(和下文的析构函数)同样是CPP的叫法。
构造函数表示为:引用:
Private Sub Class_Initialize()BTW:大家可以先在左上端的对象下拉框内选择Class,然后在右边选择Initialize
End Sub
比方说,我们希望在类被初始化,m_strName自动被赋予“IceBear”,只需:引用:
Private Sub Class_Initialize()说完构造函数,咱再说说析构函数。
m_strName = "IceBear"
End Sub
析构函数是类被销毁时要进行的操作,通常用于清理、释放等操作。他的函数名是:引用:
Private Sub Class_Terminate()析构函数的用法和构造函数相似,这里不再阐述。
缺憾
VB的类的缺陷是比较多的,就像我们刚开始了解的那样,他不能够继承,而且用类模块其实是一件挺烦的事。相对于其他OOP语言来说,VB的类还不支持真正的多态(VB是通过多重 ActiveX 接口来提供多态的),而友元效果也不太令人满意。
还有一点比较重要,就是在类模块中是不能使用子类化(SubClass)和钩子(Hook)的。(除非你会用ASM写一个处理子程,然后挂接)
尾声
至此,我们已经了解了VB类的大体使用方法,大家如果对此感兴趣的话,可以自己通过MSDN学习:事件(events)、友元(Friend:VB中是这样)等更高级的技术。
咳...咳...上校顺便插段AD:
http://bbs.cfan.com.cn/viewthrea ... p%3Bfilter%3Ddigest
这里是上校自己写的托盘图标类,大家有兴趣可以看看Source Code。
——————————————————————————————————————————————————————
如果以上出现什么错误,请告诉我~Thanks~
本文所有代码: