向美术教师交作业时,一位学生只交了一张白纸。
老师问:”画呢?”
学生答:”这儿?”他指着白纸说。
老师:”你画的是什么?”
学生:”牛吃草。”
老师:”草呢?”
学生:”牛吃光了。”
老师:”牛呢?”
学生:”草吃光了,牛还站在那里干什么?”
一、抽象基类
正如有个好爹可以少奋斗二十一样,父类本身是非常有用的,可以进行实例化,也可以不能实例化,做个样子,好处很大。
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 特性是不合法的,因为它定义类而不是实例方法。