vb类的继承不好做,要用多态,想请各位讲一下,多类与继承有何区别?

解决方案 »

  1.   

    implements为VB开发提供了伪继承的可能,
    却不能真正达到继承能够重用代码的真正功效。
    不过已经能够解决类的叠代问题,提高了类应用的灵活度,也许这就是你所说的多态需要体会其中的奥妙,应该看看MSDN提供的例子
      

  2.   

    多态意味着许多类可以提供同样的属性或者方法,而且调用者在调用这些属性或方法之前,不必知道某个对象属于什么类。 
    例如,Flea 类跟 Tyrannosaur 类可能都有 Bite 方法。多态意味着可以调用 Bite 方法,而不必知道某个对象是 Flea 还是个 Tyrannosaur —尽管以后当然会搞清这一点的。 
        下面的主题是围绕着 Visual Basic 实现多态的途径,以及怎样在程序中使用它而展开的。 
        Visual Basic 是如何提供多态的 大多数面向对象的语言,都是通过继承来提供多态的;而 Visual Basic 则是用部件对象模型 (COM) 的多接口方法。创建和实现接口用一个扩展的代码示例,显示了怎样为 Tyrannosaur 和 Flea 类来创建抽象的 Animal 接口,并实现它。 实现属性
        所实现的接口,除了具有方法以外,也具有属性,尽管在实现属性的途径方面有一些差异。 关于对象和接口的简要补充讨论把术语对象和接口搞清,为接口引入了查询的概念,并对其它接口源的实现作了描述。 代码重用的许多(内部)方面 除了实现抽象的接口外,也可以通过实现一个普通类的接口来重用代码,然后选择性地委派类的一个隐藏实例为代表。 大多数面向对象的程序设计系统,都是通过继承来提供多态的。也就是说,设想的 Flea 和 Tyrannosaur 类可能都是从某个 Animal 类继承来的。每个类都将重写 Animal 类的 Bite 方法,以提供自己的噬咬特点。     多态来自于这个事实:可以调用属于某类对象的 Bite 方法—该类可以是从 Animal 中派生出来的任意类,而不必知道该对象到底属于哪一个类。 提供多态接口
        Visual Basic 不用继承来提供多态。Visual Basic 是通过多重 ActiveX 接口来提供多态的。在构成 ActiveX 规格说明基础的组件设计模型 (COM) 中,多重接口允许软件部件系统在不扩散现有代码的情况下进行展开。 一个接口是一组相关的属性和方法。ActiveX 规格说明的许多内容是关于实现一些标准接口的, 这些标准接口是用来获得系统服务或者为其它程序提供功能。在 Visual Basic 中,可以创建一个 Animal 接口,并用自己的 Flea 和 Tyrannosaur 类予以实现。然后就可以调用任何一种对象的 Bite 方法,而无须知道它到底是哪一种对象。 多态和性能 
        从性能方面考虑,多态是很重要的。为了认识这一点,考虑下面的函数: Public Sub GetFood(ByVal Critter As Object, _ 
      ByVal Food As Object) 
        Dim dblDistance As Double 
        '计算到食物所在处距离的代码(略)。 
        Critter.Move dblDistance ' 后期绑定 
        Critter.Bite Food ' 后期绑定 
    End Sub     对 Critter 来说,Move 方法和 Bite 方法是后期绑定的。后期绑定在 Visual Basic 编译时不能决定变量包含何种对象时发生。在本示例中,Critter 参数被声明为 As Object,因此在运行时,它可能包含对任何类型对象引用,如 Car 或者 Rock。因为它不可能指明对象将是什么,所以 Visual Basic 编译一些附加的代码,用这些代码来询问该对象是否支持已经调用的方法。如果该对象支持这种方法的话,那么附加的代码将调用它;反之,附加的代码将会产生一个错误。每种方法或者属性的调用都会引入这个额外开销。 
        相比较而言,接口则允许前期绑定。当 Visual Basic 在编译时明确知道正在调用什么样的接口时,它将检查一下类型库,看看那个接口是否支持该方法。然后 Visual Basic 就可以用一张虚拟函数表 (vtable),按直接跳转到该方法进行编译。这样做比起后期绑定来要快许多倍。 
        现在,假设 Move 和 Bite 方法属于 Animal 接口,而且动物的所有类都提供该接口。Critter 参数现在就可以被声明为 Animal 了,而且 Move 和 Bite 方法也将是前期绑定的: Public Sub GetFood(ByVal Critter As Animal, _ 
      ByVal Food As Object) 
        Dim dblDistance As Double 
        '计算到食物所在处距离的代码(略)。 
        Critter.Move dblDistance '前期绑定 (vtable)。 
        Critter.Bite Food '前期绑定 (vtable)。 
    End Sub 在下面“创建和实现接口”中,创建了一个 Animal 接口,并在 Flea 和 Tyrannosaur 类中实现它。 创建和实现接口 
        如上面“Visual Basic 是如何提供多态的(1)”中所解释的,所谓接口,就是一组属性和方法。在下面的代码示例中,将创建一个 Animal 接口,并在两个类— Flea 和 Tyrannosaur 中实现它。
        创建该 Animal 接口的方法是:将一个类模块添加到工程中,将它命名为 Animal,并插入到下面的代码中: Public Sub Move(ByVal Distance As Double) 
    '加一行注释是为了防止编译器忽略这个过程
    End Sub Public Sub Bite(ByVal What As Object) 
    '加一行注释是为了防止编译器忽略这个过程 
    End Sub     注意,在这些方法中并没有任何代码。Animal 是一个抽象类,不包含实现的任何代码。抽象类不是用来创建对象的—其用途是为添加其它类中的接口提供模板。(尽管,已经证明,有些时候实现一个非抽象类的接口是有用的;这将在本节主题的后面进行讨论。)注意 确切地说,一个抽象的类是不能从中创建对象的类。从 Visual Basic 的类中总是可以创建对象的,即使它们不含有代码,它们也不是真正抽象的。 
        现在,就可以添加另外两个类模块了,这两个类模块一个叫 Flea,另一个叫 Tyrannosaur。为了在 Flea 类中实现 Animal 接口,要用到 Implements 语句: Option Explicit 
    Implements Animal     一旦将这行代码添加进去,就可以单击代码窗口中左边(“对象”)下拉菜单。其中一个登录项将是 Animal。当选择了它时,右边(“过程”)下拉菜单上将显示 Animal 接口的方法。依次选择每种方法,可为所有的方法创建空白过程模板。这些模板将具有正确的参数和数据类型,就象在 Animal 类中所定义的那样。每个过程名都将以 Animal_ 前缀来标识该接口。
        重点 一个接口就象一个契约。实现接口后,当调用该接口的任何属性或者方法时,一个类已经约定好要作出反应。因此,必须实现接口的所有属性和方法。 
      

  3.   

    现在,就可以将下面的代码添加到 Flea 类中了: Private Sub Animal_Move(ByVal Distance As Double) 
      '(跳过多少英寸的代码,略。) 
      Debug.Print "Flea moved" 
    End Sub Private Sub Animal_Bite(ByVal What As Object) 
      '(吃了多少生命的代码,略。) 
      Debug.Print "Flea bit a " & TypeName(What) 
    End Sub     可能想知道为什么这些过程被声明为 Private。如果它们是 Public,那么 Animal_Jump 和 Animal_Bite 过程将成为 Flea 界面的一部分,这样将被困在跟原先所在的同样的圈子中,将 Critter 参数声明为 As Object,这样它就可能包含一个 Flea 或者一个 Tyrannosaur。 
    多重接口 
        现在,Flea 类有了两个接口:刚刚实现的 Animal 接口—它有两个成员,以及缺省的 Flea 接口—它没有任何成员。该示例的后面,将把一个成员添加到缺省接口的其中一个。类似地,可以为 Tyrannosaur 类实现 Animal 接口: Option Explicit 
    Implements Animal Private Sub Animal_Move(ByVal Distance As Double) 
      '(跳起多少码的代码,略。) 
      Debug.Print "Tyrannosaur moved" 
    End Sub Private Sub Animal_Bite(ByVal What As Object) 
      '(拿起一磅肉的代码,略。) 
      Debug.Print "Tyrannosaur bit a " & TypeName(What) 
    End Sub 练习 Tyrannosaur 和 Flea 
    将下列代码添加到“Form1”的 Load 事件中: 
    Private Sub Form_Load() 
      Dim fl As Flea 
      Dim ty As Tyrannosaur 
      Dim anim As Animal   Set fl = New Flea 
      Set ty = New Tyrannosaur 
      '首先看一下 Flea。 
      Set anim = fl 
      Call anim.Bite(ty) 'Flea 叮咬 dinosaur。 
      '现在轮到 Tyrannosaur。 
      Set anim = ty 
      Call anim.Bite(fl) 'Dinosaur 咬 flea。 
    End Sub 
        按 F8 键,单步执行代码。注意“立即”窗口中的信息。当 变量 anim 包含对 Flea 的引用时,就调用该 Flea 的 Bite 实现,同样的,对于 Tyrannosaur 来说,情况也是如此。变量 anim 可以包含对实现 Animal 接口的任何对象的引用。事实上,它只能包含对这种类型对象的引用。如果试图将一个 Form 或者一个 PictureBox 对象赋给 anim 的话,那么将会产生错误。当通过 anim 来调用 Bite 方法时,该方法是前期绑定的,因为 Visual Basic 在编译时知道,不论将什么对象赋给 anim,该对象都将有一个 Bite 方法。 
        将 Tyrannosaurs 和 Fleas 传递给过程 
    回忆一下“Visual Basic 如何提供多态?”("How Visual Basic Provides polymorphism?")中的 GetFood 过程。可以将 GetFood 过程的第 2 个版本—演示多态的版本—添加到“Form1”中,并用下面的代码来取代 Load 事件中的代码: Private Sub Form_Load() 
    Dim fl As Flea 
    Dim ty As Tyrannosaur 
      Set fl = New Flea 
      Set ty = New Tyrannosaur 
      'Flea 在恐龙上吃饭。 
      Call GetFood(fl, ty) 
      '相反的情况。 
      Call GetFood(ty, fl) 
    End Sub   单步执行上述代码,显示作为传递给另一接口类型参数的对象引用,是怎样转换为对第二个接口(在这里是 Animal)的引用的。所发生的情况是: Visual Basic 查询该对象,看看该对象是否支持第二个接口。如果该对象支持的话,它将返回对该接口的引用,于是 Visual Basic 将返回的引用放置到参数变量中。如果该对象不支持第二个接口,将会出现错误。 实现返回值的方法 
        假设 Move 方法返回一个值。不管怎么说,自己知道想让 Animal 移动多远的距离,但是,各个实例不可能都移动那么远。它可能又老又弱,或者在路上可能恰好有一堵墙。可以用 Move 方法的返回值来说明该 Animal 实际移动了多远的距离。 
    Public Function Move(ByVal Distance As Double) As Double End Function 在 Tyrannosaur 类中实现该方法时,把返回值赋给过程名,这跟处理任何其它 Function 过程一样: Private Function Animal_Move(ByVal Distance As Double) As Double 
      Dim dblDistanceMoved As Double 
      '计算能弹跳多远(基于对年龄、健康状态和障碍物的 
      '考虑)的代码,略。 
      '该示例假设已经将结果放置到变量 dblDistanceMoved 中。 
      Debug.Print "Tyrannosaur moved"; dblDistanceMoved 
      Animal_Move = dblDistanceMoved 
    End Function 为了返回值的赋值,使用全过程名,包括接口前缀。