《VB接口与抽象类-.pdf》由会员分享,可在线阅读,更多相关《VB接口与抽象类-.pdf(17页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、目录VB接口的概念 .1接口要点.1示例一:IDisplayer.2示例二:ISpeak.8示例三:实现多态(Animal).10示例四:IEnumerator.11示例五:Implements 语句.13.NET抽象类 .13创建 VB.NET抽象类.14实际操作中使用 VB.NET抽象类.15示例.15.NET接口和.NET抽象类的相同点和不同点.16相同点.16不同点.16.NET接口和.NET抽象类的规则与场合.17VB接口的概念2006-09-29 11:12 谢谢妖居前辈写的文章,原文地址http:/ 只支持单继承的 VB.NET为了解决多继承的问题,引入了接口的概念。我们可以这样
2、来为接口下一个定义:接口就是指只包含虚成员的虚类。1.虚类,表明了 接口是不能够被直接实例化的。也就是说,接口只是一个抽象概念。比如我们说车可以跑,人可以跑,马可以跑。我们可以看到实例化的车、人、马。但是我们可以定义一个“会跑的物质”。他可以是车,也可以使人、马,但是我们却不能说“这个东西就是一个会跑得物质,但是它并不是车、人或者马。”2.只包含虚成员,表明了 接口只是说明了它具有什么样的功能,可以提供什么样的信息。但是这些功能和信息究竟是什么,如何提供我们无法得知。就像是“会跑的物质”,我们知道它可以跑,但是具体他怎么跑我们就不知道了。之所以说接口可以部分替代多继承,就是因为VB.NET只允
3、许一个类继承自另一个,且只能是这个类;但是一个类可以实现一个或多个接口。由于接口不实现成员,只声名成员,所以也就不存在多继承的路径问题了。接口要点1、定义接口名,应按照惯例,将接口名的第一个字母命名为I,如上例中的ISpeak,这样做的目的是见名知义,原因是因为接口的英语单词是Interface;2、由于接口中的 成员签名必须在实现该接口的类中给出其具体实现,所以它们前面不用加访问级别修饰符(Public、Privated 等);3、类通过Implements 关键字实现接口;4、一个类要实现一个接口,就必须实现该接口中定义的所有成员签名;5、一个类可以实现多个接口,方法是在接口名用“,”隔开
4、,如 Implements ISpeak,IRun。现在我们假定您已经知道了接口的声名以及相关的一些基本知识,我们来看看什么时候我们需要使用接口。示例一:IDisplayer 当我们面临一个问题,就是我们有一个功能,它需要操作不同的类的实例去完成一个目的相同的方法的时候,我们就可以把这些目的相同的方法作为接口来实现。现在我们看看我们面临的问题。目前我们手头有一些类,它们之间没有继承关系,但是这些类都可以被显示成字符串。图书类。可以显示的是书名。PublicClass BookInherits Media Private m_Name As StringPublicFunction Displa
5、y()As StringReturn m_Name End FunctionEnd ClassLCD显示器类,可以显示的是显示器屏幕上面的内容。PublicClass LCDInherits ComputerService Private m_DisplayComment As StringPublicFunction Display()As StringReturn m_DisplayComment End FunctionEnd Class 用户类,显示的是全名(姓+名)。PublicClass UserInherits Person Private m_FirstName,m_LastN
6、ame As StringPublicFunction Display()As StringReturn m_FirstName&.&m_LastName End FunctionEnd Class现在我们希望我们的程序(函数)能够把这些显示内容通过Console输出到控制台上面。由于它们不是同一个类继承的,所以我么现在有两种选择:1.为每一个类做一个函数,分别对应着一个类的显示函数:代码复杂,而且如果新加入了别的类,我们不得不在做一个函数。2.使用一个函数,用Object 代替这些类,使用晚期绑定实现:不安全,如果开发者传递了一个没有相应方法的实例进取就会引发异常。现在我们使用接口看看。接口
7、是不依照类的继承关系存在的,所以我们需要首先定义一个接口。它包含了一个Display方法。这说明了符合这个接口的所有实例必然有这样的一个方法,名字叫做Display,没有参数,返回字符串:PublicInterfaceIDisplayerFunction Display()As StringEnd Interface这个 Display方法只是一个虚函数,没有内容,因为我们并不知道他们应该怎么被 Display。但是我们能够保证,他可以被Display,这样就足够了。现在我们使用这个接口来封装我们的三个类。让他们实现这个接口,实现的同时我们也必须实现接口里面的所有虚程序。这相当于告诉编译器,我
8、的类符合接口规定的功能,我能 Display,我来告诉你怎样Display。图书类。可以显示的是书名。PublicClass BookInherits Media Implements IDisplayer Private m_Name As StringPublicFunction Display()As StringImplements IDisplayer.Display Return m_Name End FunctionEnd Class LCD 显示器类,可以显示的是显示器屏幕上面的内容。PublicClass LCDInherits ComputerService Impleme
9、nts IDisplayer Private m_DisplayComment As StringPublicFunction Display()As StringImplements IDisplayer.Display Return m_DisplayComment End FunctionEnd Class 用户类,显示的是全名(姓+名)。PublicClass UserInherits Person Implements IDisplayer Private m_FirstName,m_LastName As StringPublicFunction Display()As Strin
10、gImplements IDisplayer.Display Return m_FirstName&.&m_LastName End FunctionEnd Class 显示函数。PublicSub Display(ByVal idr As IDisplayer)MsgBox(idr.Display)End Sub我们使用了参数 idr,这个参数的类型是一个接口IDisplayer。我们 使用接口可以像使用类一样。实际上我们 传递进来的是实现了这个接口的某个类的实例,但是这并不是我们关心的。我们只要知道,这个类可以Display就足够了。所以我么只需要直接调用接口函数Display,就可以调用
11、到这个接口实例里面的Display函数。他肯定存在,因为他实现了接口。如果不存在,编译器就会报错的。这样我们就可以在不知道实例类型的情况下使用方法了,而且它很安全。如果我们需要加入一个新的类,比如是 Company类,我们只要让他也实现了这个接口,就可以直接适用这个函数了。接口也允许继承,而且 允许多继承,但是接口只能从接口继承。比如我们的IDisplayer接口继承了两个.NET的接口。PublicInterfaceIDisplayerInheritsICloneable,IComparerFunction Display()As StringEnd Interface一个是 IClonea
12、ble,他表示我们的接口支持复制(克隆);另一个是 IComparer,他表示我们的接口支持比较。现在我们这三个类就出现了编译错误,因为我们现在只实现了IDisplayer的虚函数 Display,基接口(ICloneable,IComparer)的虚函数(Compare,Clone())我们还没有实现。所以我们的还必须实现基接口的虚成员。我们以Book 为例,需要稍加改动:图书类。可以显示的是书名。PublicClass BookInherits Media Implements IDisplayer Private m_Name As String PublicSub New(ByVal
13、Name As String)m_Name=Name End SubPublicFunction Display1()As StringImplements IDisplayer.Display Return m_Name End FunctionPublicFunction Compare(ByVal x As Object,ByVal y As Object)AsIntegerImplements System.Collections.IComparer.Compare Dim bx,by As Book IfTypeOf x Is Book AndAlso TypeOf y Is Boo
14、k Then bx=CType(x,Book)by=CType(y,Book)Return String.Compare(bx.m_Name,by.m_Name)End IfEnd FunctionPublic Function Clone()AsObject Implements System.ICloneable.Clone Return New Book(m_Name)End FunctionEnd Class图书类实际上包含了三个接口:IDisplayer、ICloneable和 IComparer。但是我们使用的时候,ICloneable和 IComparer接口不会出现,它的函数会
15、被当作IDisplayer来实现。PublicSub Display(ByVal idr As IDisplayer)MsgBox(idr.Display)Dim o As Object =idr.Clone End Sub当我们发现一些毫不相干的类,却有一个共同的操作,他的参数和返回值一致,而我们恰恰要在某一个(或几个)地方频繁的使用的时候,我们不妨将这些相同的部分用接口实现。但是前提条件是这些操作来设计逻辑来讲却是属于相同的操作。不要为了使用接口而使用它。以下为完整代码:PublicInterfaceIDisplayerInheritsICloneable,IComparer 此二接口在命
16、名空间System.Collections下Function Display()As String 表明接口 IDisplayer中有函数Display(),此函数具体内容在每个类中不同。End InterfacePublicClass MediastatementEnd ClassPublicClass ComputerServicestatementEnd ClassPublicClass PersonstatementEnd Class 图书类。可以显示的是书名。PublicClass BookInheritsMediaImplements IDisplayerPrivate m_Nam
17、e As StringPublicFunction Display()As StringImplements IDisplayer.Display 接口中函数的具体形式Return m_Name End FunctionPublicSub New(ByVal Name As String)m_Name=Name End SubPublicFunction Compare(ByVal x As Object,ByVal y As Object)AsIntegerImplements System.Collections.IComparer.Compare Dim bx,by As BookIfT
18、ypeOf x IsBook AndAlso TypeOf y IsBook Then bx=CType(x,Book)by=CType(y,Book)Return String.Compare(bx.m_Name,by.m_Name)End IfEnd FunctionPublic Function Clone()AsObject Implements System.ICloneable.Clone Return NewBook(m_Name)End FunctionEnd ClassLCD显示器类,可以显示的是显示器屏幕上面的内容。PublicClass LCDInheritsComput
19、erServiceImplements IDisplayerPrivate m_DisplayComment As StringPublicFunction Display()As StringImplements IDisplayer.Display Return m_DisplayComment End FunctionPublicSub New(ByVal Name As String)m_DisplayComment=Name End SubPublicFunction Compare(ByVal x As Object,ByVal y As Object)AsIntegerImple
20、ments System.Collections.IComparer.Compare Dim bx,by As LCDIfTypeOf x IsLCDAndAlso TypeOf y IsBook Then bx=CType(x,LCD)by=CType(y,LCD)Return String.Compare(bx.m_DisplayComment,by.m_DisplayComment)End IfEnd FunctionPublic Function Clone()AsObject Implements System.ICloneable.Clone Return NewLCD(m_Dis
21、playComment)End FunctionEnd Class 用户类,显示的是全名(姓+名)。PublicClass UserInheritsPersonImplements IDisplayerPrivate m_FirstName,m_LastName As StringPrivate fullname As StringPublicFunction Display()As StringImplements IDisplayer.Display fullname=m_FirstName&.&m_LastName Return fullname End FunctionPublicSu
22、b New(ByVal Name As String)fullname=Name End SubPublicFunction Compare(ByVal x As Object,ByVal y As Object)AsIntegerImplements System.Collections.IComparer.Compare Dim bx,by As UserIfTypeOf x IsUser AndAlso TypeOf y IsUser Then bx=CType(x,User)by=CType(y,User)Return String.Compare(bx.fullname,by.ful
23、lname)End IfEnd FunctionPublic Function Clone()AsObject Implements System.ICloneable.Clone Return NewUser(fullname)End FunctionEnd Class 示例二:ISpeak 本文提供的 VB.NET接口范例希望能为读者建立起对接口一个初步的、正确的认识。本例定义了一个名为ISpeak 的接口,其中包括了三个签名。由于接口是代表一种约定或者说是一种规则,它并不包含这种约定或者规则的具体实现。所以,我们只需要在接口中定义签名即可。在该接口中,包括了三个成员签名,它们分别代表了属
24、性、方法以及函数。接下来,我又定义了三个类,分别是Baby、Children 和 Adlut,这个三类都实现了上述接口。并且,在每个类实现接口的成员时,都给出了具体的实现。来看代码:定义一个接口PublicInterfaceISpeak 仅定义签名,不包含任何实现ReadOnly Property CanSpeak()As Boolean 属性Sub Speak()方法Function GetSpeakLevel()As SpeakLevelEnum 函数End Interface 说话水平枚举PublicEnumSpeakLevelEnum Bad 很差 Ordinary 一般 Fluent
25、 流利End EnumPublicClassBaby:ImplementsISpeak 通过 Implements 关键字实现接口 以下是实现该接口的所有成员PublicReadOnly Property CanSpeak()As Boolean ImplementsISpeak.CanSpeak GetReturnFalseEnd GetEnd PropertyPublicFunction GetSpeakLevel()As SpeakLevelEnum ImplementsISpeak.GetSpeakLevel ReturnSpeakLevelEnum.Bad End FunctionP
26、ublicSub Speak()ImplementsISpeak.Speak Console.WriteLine(旁白):他是一个婴儿,还不会说话。)End SubEnd ClassPublicClassChildren :ImplementsISpeakPublicReadOnly Property CanSpeak()As Boolean ImplementsISpeak.CanSpeak GetReturnTrueEnd GetEnd PropertyPublicFunction GetSpeakLevel()As SpeakLevelEnum ImplementsISpeak.GetS
27、peakLevel ReturnSpeakLevelEnum.Ordinary End FunctionPublicSub Speak()ImplementsISpeak.Speak Console.WriteLine(哈哈,我是一个快乐的孩子!)End SubEnd ClassPublicClassAdult :ImplementsISpeakPublicReadOnly Property CanSpeak()As Boolean ImplementsISpeak.CanSpeak GetReturnTrueEnd GetEnd PropertyPublicFunction GetSpeak
28、Level()As SpeakLevelEnum ImplementsISpeak.GetSpeakLevel ReturnSpeakLevelEnum.Fluent End FunctionPublicSub mySpeak()ImplementsISpeak.Speak Console.WriteLine(OK,我是成年人了,不但能流利地说母语,还会说外语。)End SubEnd Class 程序入口点PublicClassAppStartShared Sub Main()Dim person1 As New BabyDim person2 As New ChildrenDim perso
29、n3 As New Adult person1.Speak()person2.Speak()person3.mySpeak()If person2.GetSpeakLevel SpeakLevelEnum.Bad ThenConsole.WriteLine(Person2 的说话水平不算很差)End IfIf person1.CanSpeak ThenConsole.WriteLine(Person1 会说话)ElseConsole.WriteLine(Person1 不会说话)End IfConsole.Read()End SubEnd ClassPublicClassForm1Privat
30、eSub Button1_Click(sender As Object,e As EventArgs)Handles Button1.Click AppStart.Main()End SubEnd Class在 VS2005/2008 中建立一个“控制台项目”项目,将默认的Module1.vb 删除,在该项目中添加一个类,将上述代码复制到这个类中覆盖原有内容,即可运行调试。示例三:实现多态(Animal)VB.NET接口实现多态,能够通过使用多接口,用户可以在不中断运行代码的情况下,允许运行多种软件的系统组件。接口象类那样描述属性和方法,但是它和类不同的是,接口不能提供任何实现。为了正确的操作
31、 VB.NET接口实现多态,用户需先建立一个接口,并且通过其他的几个类实现该接口。用户可以用几乎相同的方法调用其他对象已经实现的方法。下面这个例子就是VB.NET接口实现多态:Namespace PolyNamespace InterfaceAnimalSub Move(ByRef Distance As Double)Sub Bite(ByVal What As Object)End InterfaceClass FleaImplements animalPublicSub bite(ByVal What As Object)Implements Animal.Bite Bite somet
32、hing End SubSub Move(ByRef Distance As Double)Implements Animal.Move Distance=Distance+1 End SubEnd ClassClass DogImplements animalPublicSub bite(ByVal What As Object)Implements Animal.Bite Bite something End SubSub Move(ByRef Distance As Double)Implements Animal.Move Distance=Distance+100 End SubEn
33、d ClassEnd Namespace add this section to the your form ProtectedSub Button1_Click(ByVal sender As System.Object,ByVal e As System.EventArgs)Dim aFlea As New Flea()Dim anobj As Object()Dim aDog As New Dog()GetFood(aflea,anobj)GetFood(aDog,anobj)End SubPublicSub GetFood(ByVal Critter As Animal,ByVal F
34、ood As Object)Dim dblDistance As DoubleCode to calculate distance to food(omitted).Critter.Move(dblDistance)Early bound(vtable)Critter.Bite(Food)Early bound(vtable)End Sub 示例四:IEnumerator 在面向对象的设计中,经常会用到有类似父子关系的这个对象,比如在一个项目中,有订单对象,在一个订单下又包含多个产品,这时我就想用Iterator 模式来封装订单下的产品,在.Net 中的 IEnumerator 接口就是用来实
35、现迭代的,来支持.Net 中的 for each 的操作。要 VB.NET实现 IEnumerator 接口,需在实现以下几个函数来支持IEnumerator接口的操作OverridableReadOnly Property Current()As ObjectCurrent 用于在迭代过程中得到当前的对象。PublicOverridableFunction MoveNext()As BooleanMoveNext 用于在迭代过程中将迭代指针指向下一个对象,初始是迭代指针指向集合的开始(在第一个节点之前的位置),一旦越过集合的结尾,在调用Reset 之前,对MoveNext 的后续调用返回fa
36、lse。OverridableSub Reset()将枚举数设置为其初始位置,该位置位于集合中第一个元素之前。只要集合保持不变,枚举数就将保持有效。如果对集合进行了更改(例如添加、修改或删除元素),则该枚举数将失效且不可恢复,并且下一次对MoveNext 或 Reset 的调用将引发 InvalidOperationException。下面是一个具体的实现IEnumerator 接口的对象:Imports System.Collections 在此实际实现的是 System.Collections.IEnumerable接口,IteratorProduct 用此接口来向使用者提供对 IEnum
37、erator 接口的操作。PublicClass IteratorProduct:Implements System.Collections.IEnumerable Private Products As Collection 用Collection在存订单中的所有产品Private item As Integer =-1 PublicSub New()Products=New Collection Products.Add(xh)这只是为了测试方便,将加入产品的内容直接写在这了 Products.Add(lj)Products.Add(qd)End SubOverridableReadOnl
38、y Property Current()As ObjectGetReturn Products(item)End GetEnd PropertyPublicOverridableFunction MoveNext()As Boolean item+=1 End FunctionOverridableSub Reset()item=-1 End Sub 返回迭代对像给使用者OverridableFunction GetEnumerator()As IEnumerator ImplementsIEnumerable.GetEnumerator Return Me.Products.GetEnume
39、rator End FunctionEnd Class 示例五:Implements语句PublicInterfaceICustomerInfoEvent updateComplete()Property customerName()As StringSub updateCustomerStatus()End InterfacePublicClass customerInfoImplements ICustomerInfo Storage for the property value.Private customerNameValue As StringPublicEvent updateCo
40、mplete()Implements ICustomerInfo.updateComplete PublicProperty CustomerName()As String _ Implements ICustomerInfo.customerName GetReturn customerNameValue End GetSet(ByVal value As String)The value parameter is passed to the Set procedure when the contents of this property are modified.customerNameV
41、alue=value End SetEnd PropertyPublicSub updateCustomerStatus()_ Implements ICustomerInfo.updateCustomerStatus Add code here to update the status of this account.Raise an event to indicate that this procedure is done.RaiseEvent updateComplete()End SubEnd Class类 customerInfo 在单独的源代码行上使用Implements 语句,以
42、指示该类实现 ICustomerInfo 接口的所有成员。然后,该类中的每个成员使用Implements 关键字作为其成员声明的一部分,以指示它实现该接口成员。.NET抽象类抽象类即是不能被实例化的类,而且你不能够在自己的程序里通过这种类来生成一个对象,必须通过继承由派生类实现其抽象方法,因此对抽象类 不能使用new 关键字,也不能被密封。抽象类提供多个派生类共享基类的公共定义,它既可以提供抽象方法,也可以提供非抽象方法。如果派生类没有实现所有的抽象方法,则该派生类也必须声明为抽象类。你也许会认为不能被实例化的类是没多大用处的。事实上,在某些环境下,抽象类是非常有用的。你可以把它作为一个基础类
43、以此为基类创建新的类。在此假设一个要用抽象类的程序设计。假设图形程序提供了大量的drawing类:比如圆,三角,曲线等等。所有这些作 drawing 类都有某个共同的特性,比如位置,大小,色彩,删除等属性。其它的类成员,比如描绘图形的方法就不同于每个单独的类。因此,这样为利用抽象类创造了一个理想的环境。在抽象类中,你需要定义所有 drawing 类都需要的全部通用类成员。因为drawing 类不响应任何图形。但是当把它作为shape-specific drawing类中的基类,你需要确认所有这些抽象类拥有最基本的功能.创建 VB.NET 抽象类对抽象类的定义很简单,只要在定义域里加入关键字Mu
44、stInherit,如:PublicMustInheritClass MyAbstractclass.End Class 除开要包含关键字之外,抽象类的定义与非抽象类的定义没什么两样。抽象类包含了从其它类承继而来的属性及方法等等。但是,在抽象类中定义的类成员(属性及方法)是如何与衍生类中的定义元素产生联系的呢?在此并没有复杂的规则,但你作出的决定将与你所拥有的类及它将衍生出来的类的性能密切相关。你有三个选择。1、抽象类中已有的功能比如属性、方法等在衍生类中可以被重载。实现这一功能只要在基类成员定义中输入Overridable 关键字。如:PublicOverridableSub Move(Ne
45、wX As Integer,NewY As Integer).End Sub 2、为重载抽象类中的成员,衍生类在执行语句中须使用Overrides 关键字。如:InheritsMyAbstractclassPublicOverridesSub Move(NewX As Integer,NewY As Integer).End Sub 3、抽象类中已有的功能不能在衍生类中重载。即衍生类必须使用抽象类定义的成员。如果你在抽象类中不使用任何特殊字段来定义某一成员将会使其产生缺省。以下代码定义了抽象类中的Xpos属性,衍生类必须使用该属性,因为该属性不能被重载:PublicProperty XPos(
46、)As Integer .End Property 利用 MustOverride 关键字来定义 VB.NET抽象类成员,该成员可以在衍生类中重载。以下举例说明了衍生类必须执行名为GetObjectAt()的方法。PublicMustOverrideFunction GetObjectAt(X As Integer,Y As Integer)As Object 使用 MustOverride 最方便的地方在于它 不仅规定了成员的名称同样也规定了成员的符号。先前的例子要求任一衍生类都需定义名称为GetObjectAt()的方法,因此会产生两种类型的整型变量以及返回一个类型对象。该方法在衍生类中的
47、执行是完全根据程序员的指令要求。通过MustOverride 字段说明的方法一些时候也被称为 abstract 成员。实际操作中使用VB.NET 抽象类设想一下你正在为一家大公司开发一个新的员工数据库。你的工作就是监督类的执行,使雇员的基本情况与管理部门的要求相吻合,同样,在区分公司分支机构的要求时也提供更大的便利。基本要求如下:抽象类里有姓名,雇用时间等属性,这些属性不会被重载。因为大多数雇员来自美国,因此在抽象类里执行退休ID,以运行社会保障号码。国外分公司将会利用不同的方式来鉴别雇员的退休ID,因此该属性会在衍生类里重载,以便单个分支机构分别执行。名称为补偿金的方法不会引起争议,然后会返
48、回一个含有雇员补偿金细节的类型对象。因为不同分公司都规定不同的补偿金,薪金,佣金,红利等,因此灵活的执行这些对象是很有必要的,由此会产生一个抽象方法。由此而产生的抽象类的代码,称为EmployeeBase,会在第一栏里显示。位于法国的分公司的程序员用EmployeeBase类作为 EmployeeFrance类的基类,与当地雇员记录软件合作使用。因此,衍生类需要承继名称及雇用日期等字段。更多的是,在EmployeeBase类中的 RetirementID 成份适合运用在法国,因此,新类不会重载该成份。程序员做的所有这些工作是为了执行abstractCompensationmember的重载。但
49、是在英国,RetirementID属性不适用于该地,因此衍生类将会重载该成员,同样的也会重载 Compensation成员。示例PublicMustInheritClass MyFilesMyFiles 是个抽象类InheritsCollectionBaseFriendMustOverrideSub Add(ByVal MyFileName As String)Add 方法必须重载FriendSub AddAllFileNames(ByVal MyPath As String)抽象类中有部分方法可以不重载For Each SingleFileName As StringInMy.Compute
50、r.FileSystem.GetFiles(MyPath)Add(GetFileName(SingleFileName)NextEnd SubProtectedFunction GetFileName(ByVal MyFile As String)As StringProtected表示只有MyFiles 和它的派生类可以访问Return System.IO.Path.GetFileNameWithoutExtension(MyFile)End FunctionEnd ClassPublicClass NewFilesInheritsMyFilesFriendOverridesSub Add(