向美术教师交作业时,一位学生只交了一张白纸。
老师问:”画呢?”
学生答:”这儿?”他指着白纸说。
老师:”你画的是什么?”
学生:”牛吃草。”
老师:”草呢?”
学生:”牛吃光了。”
老师:”牛呢?”
学生:”草吃光了,牛还站在那里干什么?”
一、抽象基类
正如有个好爹可以少奋斗二十一样,父类本身是非常有用的,可以进行实例化,也可以不能实例化,做个样子,好处很大。
1、MustInheit
基类可实例化,但也可以不实例化(或者禁止创建该类的对象),这时,只能创建子类,以及子类的对象。
可以用MustInherit来指明该基类不能创建对象。
注意:这不表示不能声明基类(如下例Person)类型的变量,只是不能用New Peron来创建对象。
同时在这个时候,我们还可继续使用共享办法(因为它只属于类,不属于对象)
2、MustOveride
指明只能由子类重写的方法(sub,funciton,property),此类方法只有方法头,而无具体的实现代码。因此不能创建对象。
故: 类中含有MustOveride时,此类必须由MustInherit进行声明。即:
只能在属性和过程声明语句中使用 MustOverride。 指定 MustOverride 的属性或过程必须是类的成员,并且该类必须标
记为 MustInherit。
Public MustInherit Class Person '只能被继承
Private mName As String
Private mBirthDate As Date
Private mID As String
Public Event NameChanged(ByVal newName As String)
Public Event DataChanged(ByVal field As String, ByVal newValue As Object)
Public MustOverride Function LifeExpectancy() As Integer '无实现代码,只能被子类重写
'......
End Class
上面声明Mustoveride的函数LifeExpectancy只有方法头,无实现代码,此类方法也称“抽象方法”或“纯虚拟函数”
任何继承此类的子类,必须重写这些抽象方法,否则将产生语法错误,子类也不会编译。
故在子类中添加:
'=======子类Employee中重写基类的纯虚函数===============
Public Overrides Function LifeExpectancy() As Integer
Return 90
End Function
3、抽象基类
MustInherit及MustOverride同时出现该类时,此类被称为抽象基类。或者说:含有抽象方法的类叫抽象基类。
抽象基类很有用,可以前期只架上抽象基类这个架子,每个子类根据自己情况进行重写适合自己的方法。
注意的是,此时子类都必须有抽象方法的实现代码,否则出错。
Public MustInherit Class AbstractBaseClass
Public MustOverride Sub DoSomething()
Public MustOverride Sub DoOtherSomething()
End Class
'接口
Public Interface IAbstractBaseClass
Sub DoSomething()
Sub DoOtherSomething()
End Interface
某些方面,抽象基类与Interface接口概念相似,上面接口IAbstractBaseClass,在实现这接口的类中也必须有其内两个方法
的实现代码,否则一样也会出错。
4、禁止继承
上面是“只能继承”中创建对象。
下面是“禁止继承”,用NotIneritable来说明此类,不准再进行继承,到此为止!
Public NotInheritable Class officeEmployee '指定基不再做基类(即不再向下派生)
不能被继承的类有时被称为“密封”类
二、多接口
接口,如同外交家一样,可以与外界进行交流。
VB.net中,对象可以有一个或多个接口。所有对象都有一个主接口或本地接口,接口由用Public声明的方法、属性、事件、字段。
除本地接口外,对象还可以使用Implements来实现辅助接口。
1、对象接口
类中除Private声明的外都可作本地接口。例:
'方法作为接口
Public Sub AMethod()
End Sub
'属性作为接口
Public Property AProperty() As String
End Property
'Event事件作接口
Public Event AEvent()
'变量作接口
Public AInteger As Integer
注意:上面无实现代码,因为接口只在于架子的声明,其内的实现代码并不是接口的一部分。这是重要的区别!
因为将接口与实现代码区分是面向对象程序设计的核心内容。
使用本地接口
一旦声明了本地接口,就可在对象中用这些接口与外界产生交流。下面类中Pulbic方法,其对象就可直接用它与外界发生联系。
'============类的本地接口=========
Public Class TheClass
Public Sub DoSomething()
'.....
End Sub
Public Sub DoOtherSomething()
'.......
End Sub
End Class
'==========主程序中使用这两个接口========
Dim myObject As New TheClass
myObject.DoSomething()
myObject.DoOtherSomething()
2、辅助接口
在设计上,只有同类的对象才有相同的本地接口,这有很大的限制。
如果把一组不同的对象,它们本地接口各不同,处理起来就麻烦了。
比如,对于产品 、顾客、发票三类对象,不具有自然的继承关系,但是,我们需要打印这三个信息,于是,
产生了另一个概念“辅助接口”,也就平时说的接口,通过这个接口,使三者均可打印。
接口Interface是同Class,Modul一样是同一级别的概念,它象抽象方法一样并不具有实现代码,实现代码都在使用接口
的类中。这样使用接口时,不必管是不是同一类型对象,均可正常运行。
(1)定义接口
用Interface来定义一个接口,通过添加模块方式,在模块外部添加接口代码:
Public Interface IPrintableObject '接口定义(与Class,Module平级)内部作用域相同
Function Label(ByVal index As Integer) As String '故其前无Public,friend等限定
Function Value(ByVal index As Integer) As String
ReadOnly Property Count() As Integer
End Interface
Module Interfaces
End Module
接口定义包含在 Interface 和 End Interface 语句内。
在 Interface 语句后面,可以选择添加列出一个或多个被继承接口的 Inherits 语句。 即接口可以继承。
在声明中,Inherits 语句必须出现在除注释外的所有其他语句之前。
接口定义中其余的语句应该包括 Event、Sub、Function、Property、Interface、Class、Structure 和 Enum 语句。
接口不能包含任何实现代码或与实现代码关联的语句,如 End Sub 或 End Property。
在命名空间中,默认情况下,接口语句为 Friend,但也可以显式声明为 Public 或 Friend。
在类、模块、接口和结构中定义的接口默认为 Public,但也可以显式声明为 Public、Friend、Protected 或 Private。
Shadows 关键字可应用于所有界面成员。 Overloads 关键字可应用于界面定义中声明的 Sub、Function 和 Property 语句。
此外,Property 语句可以具有 Default、ReadOnly 或 WriteOnly 修饰符。 不允许使用任何其他修饰符:Public、Private、
Friend、Protected、Shared、Overrides、MustOverride 或 Overridable。
接口是一个数据类型,类似于类或结构(Struction)。因为可以这样定义这个类型的变量:
Private printable As IPrintableObject
但上面,都没有实现的代码,实现代码都是在类中实现。所以接口是不能直接用New的。
(2)使用接口
用户只须知道接口,不必了解对象内部细节,就可以使用不同的对象。
窗体中添加方法:
Public Sub PrintObject(obj As IPrintableObject) '窗体内添加例程
Dim index As Integer
For index = 0 To obj.Count
Debug.Write(obj.Label(index) & ": ")
Debug.WriteLine(obj.Value(index))
Next
End Sub
然后窗体中添加按钮,并添加代码:
Private Sub ButtonPrint_Click(sender As Object,e As EventArgs) Handles ButtonPrint.Click
Dim obj As New Employee()
obj.EmployeeNumber = 123
obj.BirthDate = #1/1/1980#
obj.HireDate = #1/1/1996#
PrintObject(obj)
End Sub
点击运行,会提示错误:因为接口只是方法头或事件头,并没有具体的实现代码。因此还要在类中添加实现的代码。
(3)实现接口
任何类(除抽象基类)都可以使用Implements来实现接口。
因为接口要用类的对象来传递,使得接口最后言之有物。而抽象基类是不能产生对象的,所以抽象基类是不能有接口的。
加入Implements回车后,会自动把接口各方法或事件的架子添加到类中,我们只须在内加入实现代码。
类中实现的方法、属性等名称,并不重要,重要的是参数的数据类型及返回值必须与接口定义的类型相匹配。
实现代码如下:
'================Employee类中实现接口(连接到外部接口类型上)=========
Implements IPrintableObject '这里按回车后,自动添加Iprintableobject成员,来实现细节
'为接口准备或要使用的字段
Private mLabels() As String = {"ID", "Age", "HireDate"}
Private mBirthDate As Date
Private mSalary As Double
Public ReadOnly Property Count As Integer Implements IPrintableObject.Count
Get
Return UBound(mLabels)
End Get
End Property
Private Function Label(index As Integer) As String Implements IPrintableObject.Label
Return mLabels(index)
End Function
Public Function Value(index As Integer) As String Implements IPrintableObject.Value
Select Case index
Case 0
Return CStr(EmployeeNumber)
Case 1
Return CStr(Age)
Case Else
Return Format(HireDate, "Short date")
End Select
End Function
注意:可以看到实现方法等的后面有Implements IPrintableObject.Label,这类似于Handles,它也可以通过用逗号把
一个接口的多个方法联系到一个方法中,或者多个接口的方法联系到一个方法中。
(实际上就是把函数地址与外界联系了起来,这样使用这个地址,自然就调用了不同对象中的方法)
看一下整个接口的示意图:
(4)重用 通用的实现代码
辅助接口,可以有几个,几个中可以同一个方法,这个方法就成了通用的方法。
下面两个接口有方法都联系到同一个方法Value(类Employe中)
'=========接口1,有Value=================
Public Interface IPrintableObject
Function Label(ByVal index As Integer) As String
Function Value(ByVal index As Integer) As String
ReadOnly Property Count() As Integer
End Interface
'========接口2,有GetValue===============
Public Interface IVales
Function GetValue(ByVal index As Integer) As String
End Interface
'=======Employee中两接口,同时关联到Value函数上(函数签名与上面相同参数)===========
Implements IPrintableObject
Implements IVales
Public Function Value(index As Integer) As String Implements IPrintableObject.Value, IVales.GetValue
Select Case index
Case 0
Return CStr(EmployeeNumber)
Case 1
Return CStr(Age)
Case Else
Return Format(HireDate, "Short date")
End Select
End Function
可以看到,其后用逗号表示,两个接口都用到Value方法。
(5)合并接口、继承
可以同时合并辅助接口和继承在。
当从一个实现接口的类继承时,新的子类自动获得的接口和基类的实现代码。
如果指定基类方法可以重写,则子类可以重写这些方法。
不仅重写基类主接口的实现代码,而且也可以重写接口的实现代码。
Public Overridable Function Value(ByVal index As Integer) As String Implements IPrintableObject.Value, IValues.GetValue
上面表示这个接口实现,是可以被重写的。
完整定义如下:
Public Overridable Function Value(index As Integer) As String Implements IPrintableObject.Value, IVales.GetValue
Select Case index
Case 0
Return CStr(EmployeeNumber)
Case 1
Return CStr(Age)
Case Else
Return Format(HireDate, "Short date")
End Select
End Function
注意:由于上面是Public,因此它在主接口(本地接口)中也是有效的。
并且它还是两个接口中的一部分,意味着可以被三种方式访问。
三种 方式访问:
Dim emp As New Employee("Andy")
Dim printable As IPrintableObject = emp
Dim values As IVales = emp
Debug.WriteLine(emp.Value(0))
Debug.WriteLine(printable.Value(0))
Debug.WriteLine(values.GetValue(0))
第一种:主接口(本地接口)
第二种:IprintableObject接口访问
第三种:IValues接口访问
这样三种接口合并于一身,都在Value这个方法上。
要声明接口方法的实现,可以使用任何在实例方法声明上合法的特性(包括 Overloads、Overrides、Overridable、Public、Private、Protected、Friend、Protected Friend、MustOverride、Default 和 Static)。
Shared 特性是不合法的,因为它定义类而不是实例方法。