Thank s to YaoJu for write this article.Help others is help oneself.
谢谢'妖居"前辈写的文章,原文地址http://farrio.cnblogs.com/archive/2005/09/09/127917.html:
只支持单继承的
VB.NET
为了解决多继承的问题,引入了接口的概念。我们可以这样来为接口下一个定义:接口就是指只包含虚成员的虚类。
(1)
虚类,表明了接口是不能够被直接实例化的。也就是说,接口只是一个抽象概念。比如我们说车可以跑,人可以跑,马可以跑。我们可以看到实例化的车、人、马。但是我们可以定义一个“会跑的物质”。他可以是车,也可以使人、马,但是我们却不能说“这个东西就是一个会跑得物质,但是它并不是车、人或者马。”
(2)
只包含虚成员,表明了接口只是说明了它具有什么样的功能,可以提供什么样的信息。但是这些功能和信息究竟是什么,如何提供我们无法得知。就像是“会跑的物质”,我们知道它可以跑,但是具体他怎么跑我们就不知道了。
之所以说接口可以部分替代多继承,就是因为
VB.NET
只允许一个类继承自另一个,且只能是这个类;但是一个类可以实现一个或多个接口。由于接口不实现成员,只声名成员,所以也就不存在多继承的路径问题了。
现在我们假定您已经知道了接口的声名以及相关的一些基本知识,我们来看看什么时候我们需要使用接口。
当我
们
面
临
一个
问题
,就是我
们
有一个功能,它需要操作不同的
类
的
实
例去完成一个目的相同的方法的
时
候,我
们
就可以把
这
些目的相同的方法作
为
接口来
实现
。
现
在我
们
看看我
们
面
临
的
问题
。目前我们手头有一些类,它们之间没有继承关系,但是这些类都可以被显示成字符串。
'
图书类。可以显示的是书名。
Public
Class Book
Inherits Media
Private m_Name As String
Public Function Display() As String
Return m_Name
End Function
End
Class
'
LCD
显示器类,可以显示的是显示器屏幕上面的内容。
Public
Class LCD
Inherits ComputerService
Private m_DisplayComment As String
Public Function Display() As String
Return m_DisplayComment
End Function
End
Class
'
用户类,显示的是全名(姓
+
名)。
Public
Class User
Inherits Person
Private m_FirstName, m_LastName As String
Public Function Display () As String
Return m_FirstName & "." & m_LastName
End Function
End
Class
现在我们希望我们的程序(函数)能够把这些显示内容通过
Console
输出到控制台上面。由于它们不是同一个类继承的,所以我么现在有两种选择。
(1)
为每一个类做一个函数,分别对应着一个类的显示函数。
(2)
使用一个函数,用
Object
代替这些类,使用晚期绑定实现。
现在看看这两种做法的问题。
(1)
代码复杂,而且如果新加入了别的类,我们不得不在做一个函数。
(2)
不安全。如果开发者传递了一个没有相应方法的实例进取就会引发异常。
现在我们使用接口看看。接口是不依照类的继承关系存在的,所以我们需要首先定义一个接口。它包含了一个
Display
方法。这说明了符合这个接口的所有实例必然有这样的一个方法,名字叫做
Display
,没有参数,返回字符串。
Public
Interface IDisplayer
Function Display() As String
End
Interface
这个
Display
方法只是一个虚函数,没有内容,因为我们并不知道他们应该怎么被
Display
。但是我们能够保证,他可以被
Display
。这样就足够了。现在我们使用这个接口来封装我们的三个类。让他们实现这个接口,实现的同时我们也必须实现接口里面的所有虚程序。这相当于告诉编译器,我的类符合接口规定的功能,我能
Display
,我来告诉你怎样
Display
。
'
图书类。可以显示的是书名。
Public
Class Book
Inherits Media
Implements IDisplayer
Private m_Name As String
Public Function Display() As String Implements IDisplayer.Display
Return m_Name
End Function
End
Class
'
LCD
显示器类,可以显示的是显示器屏幕上面的内容。
Public
Class LCD
Inherits ComputerService
Implements IDisplayer
Private m_DisplayComment As String
Public Function Display () As String Implements IDisplayer.Display
Return m_DisplayComment
End Function
End
Class
'
用户类,显示的是全名(姓
+
名)。
Public
Class User
Inherits Person
Implements IDisplayer
Private m_FirstName, m_LastName As String
Public Function Display() As String Implements IDisplayer.Display
Return m_FirstName & "." & m_LastName
End Function
End
Class
现在我们着手做我们的显示函数。
Public Sub Display(ByVal idr As IDisplayer)
MsgBox(idr.Display)
End Sub
我们使用了参数
idr
,这个参数的类型是一个接口
IDisplayer
。我们使用接口可以像使用类一样。实际上我们传递进来的是实现了这个接口的某个类的实例,但是这并不是我们关心的。我们只要知道,这个类可以
Display
就足够了。所以我么只需要直接调用接口函数
Display
,就可以调用到这个接口实例里面的
Display
函数。他肯定存在,因为他实现了接口。如果不存在,编译器就会报错的。这样我们就可以在不知道实例类型的情况下使用方法了,而且它很安全。
如果我们需要加入一个新的类,比如是
Company
类,我们只要让他也实现了这个接口,就可以直接适用这个函数了。
接口也允许继承,而且允许多继承,但是接口只能从接口继承。比如我们的
IDisplayer
接口继承了两个
.NET
的接口。
Public
Interface IDisplayer
Inherits ICloneable, IComparer
Function Display() As String
End
Interface
一个是
ICloneable
,他表示我们的接口支持复制(克隆);另一个是
IComparer
,他表示我们的接口支持比较。
现在我们这三个类就出现了编译错误,因为我们现在只实现了
IDisplayer
的虚函数
Display
,基接口的虚函数我们还没有实现。所以我们的还必须实现基接口的虚成员。我们以
Book
为例,需要稍加改动。
'
图书类。可以显示的是书名。
Public
Class Book
Inherits
Media
Implements IDisplayer
Private m_Name As String
Public Sub New(ByVal Name As String)
m_Name
= Name
End Sub
Public Function Display1() As String Implements IDisplayer.Display
Return m_Name
End Function
Public Function Compare(ByVal x As Object, ByVal y As Object) As Integer Implements System.Collections.IComparer.Compare
Dim bx, by As Book
If TypeOf x Is Book AndAlso TypeOf y Is Book Then
bx = CType(x, Book)
by = CType(y, Book)
Return String.Compare(bx.m_Name, by.m_Name)
End If
End Function
Public Function Clone() As Object Implements System.ICloneable.Clone
Return New Book(m_Name)
End Function
End
Class
图书类实际上包含了三个接口:
IDisplayer
、
ICloneable
和
IComparer
。但是我们使用的时候,
ICloneable
和
IComparer
接口不会出现,它的函数会被当作
IDisplayer
来实现。
Public Sub Display(ByVal idr As IDisplayer)
MsgBox(idr.Display)
Dim o As Object = idr.Clone
End
Sub
当我们发现一些毫不相干的类,却有一个共同的操作,他的参数和返回值一致,而我们恰恰要在某一个(或几个)地方频繁的使用的时候,我们不妨将这些相同的部分用接口实现。但是前提条件是这些操作来设计逻辑来讲却是属于相同的操作。不要为了使用接口而使用它。