继承

继承是VB6.0编程人员盼望已久的面向对象编程特性。在这套书的VB篇中,笔者不得不为每种图元都定义了颜色、线宽和线型等属性。有了继承以后,只需要在基类中定义它们,并在派生类中进行继承就行了。

对继承关系的描述,最常用的说法是,是\"is-a\"的关系,即\"是一种\"的关系。比如老虎是一种动物,香蕉是一种水果等。继承是一种很有用的面向对象特性,与它有关的概念包括基类、派生类、抽象基类、重写、遮蔽等。

基类

继承是一个体系,是多个对象之间的一种关系。在这个体系里面,相邻的两个继承层次中处于上面一个层 的那个类可那些类称为基类,又称为父类,她于下面一个层次的那个人或那些类称为派生类或子类。基类在它所属的概念层次上描述对象的特征和行为。④下面结合一个例子来说明继承关系。点、直线段、圆和圆弧等都是图元的一种,它们都有编号、颜色、线宽和线型等属性,都有绘图等方法。可以考虑建立一个图元基类CGElement,然后在它的基础上派生其他图元类。作为演示,下面建当一个简单的CGElement类。

【VB.NET】

code.vb.net
Public Class CGElement
     Private m_ ID As Integer
     Public Property ID() As Integer
          Get
               Return m_ID
          End Get
          Set(ByVal Value As Integer)
               m ID=Value
          End Set
     End Property
     Public Sub New()
     End Sub
     Public Sub Draw()
          text-blue-400">Console.WriteLine"绘图元。")
     End Sub
End Class

【VC#.NET】

code.vc#.net
public class CGElement
{
     private int m_ID;
     public int ID
     {
          get return m_ID;}
          set(m_ID=value;}
     }
     public CGElement()
     {
     }
     public void Draw()
     {
          Console.WriteLine("绘图元。");
     }

派生类

古时候有一则侧笑话是这么说的:一个人照着别人描述的样子去找千里马,他老想着这千里马眼要突、蹄要怎么的,结果找回来一只大青蛙。对了,是按图索骥。笑话的本意是要我们做事不要生搬硬套,否则要闹笑话。那么这人的问题出在哪里呢?他显然忽略了他要找的首先是马,然后才是千里马这个问题。

切换到语言环境,这个问题就是基类与派生类的关系问题。显然,这里马是基类,千里马是派生类。派生类首先从基类那里继承成员,使自己具有基类的基因,然后才有自己的区别于其他派生类的特征。关于马和千里马,大致还有以下这样一些排列组合。

(1)千里马也是马。

(2)先有马,后有千里马。

(3)想做千里马,先做马。

后面要讲到的重写和遮蔽等都在派生类中实现。在VB.NET和VC#.NET中分别用Inherits关键字和冒号(:)来表示继承关系,如:

【VB.NET】

code.vb.net
Public Class Cline
     Inherits CGElement

【VC#.NET】

code.vc#.net
public class CLine:CGElement

派生类中可以继承基类的公共成员和受保护成员。如最常用的Fom1类就继承了System.Windows.Forms名字空间中的Form类。当我们在Forml内部使用Me这个关键字来调用自身的成员时,会显示一个成员列表,如图2-1所示。在没有新建成员的情况下,该成员列表中的成员都是从基类中继承来的。

Document Image
\[\]

图2-1 Forml类的继承成员

下面创建一个CLine类,它描述直线段的特征和行为。直线段是一种图元,所以直线段类CLine与图元类CGElement之间存在继承关系。定义这种继承关系以后,CLine类就能继承CGElement类的属性和方法。下面从CGElement类派生CLine类,它继承基类的ID属性和Draw方法。

【VB.NET】

code.vb.net
Public Class CLine
     Inherits CGElement
     Private m_Begin,m_End As PointF
          '定义直线段的起点和终点属性
          ‘…
         '构造函数
         Public Sub NewO
     End Sub
End Class

【VC#.NET】

code.vc#.net
public class CLine:CGElement
{
    private PointF m_Begin,m_End;
    //定义直线段的起点和终点属性
    //…
    //构造函数
    public Cline{
    {
    }
}

在Form1类中输入下面的代码,对CGElement类和CLine类进行测试。代码中首先分别创建一个CGElement类实例ge和一个CLine类实例line;然后将line的继承成员ID属性设置为10,并在输出窗口中输出它:最后绘制直线段line。

【VB.NET】

code.vb.net
Private Sub Form1_Load(ByVal sender As System.Object,
            ByVal text-blue-400">e As System.EventArg) Handles MyBase.Load
    Dim line As New Cline()
    line.ID =10
    text-blue-400">Console.text-blue-400">WriteLine("直线段的ID号为:{0}", line.ID)
    line.Draw()
End Sub

【VC#.NET】

code.vc#.net
private void Form1_Load(object sender, System.EventArgs e)
{
    CLine line=new Cline();
    line.ID=10;
    Console.WriteLine("直线段的ID号为:{0}",line.ID);
    line.Draw();
}

运行程序,输出窗口中的结果如图2-2所示。

Document Image
\[\]

图2-2输出窗口中的结果

抽象基类

前面创建的CGElement类作为CLine类的基类是可以实例化的,可以用New关键字或new关键字创建它的实例。但如果现在只想把CGElement类作为一个基类使用,因为作为\"图元\"来讲,它只是一个抽象的概念,并没有可见的形体,所以创建一个它的实例并没有多大的实际意义。

将基类创建为抽象基类,使用VB.NET时需要用到Mustinherit 关键字,使用VC#.NET时需要用到abstract关键字。必须重写的抽象方法需要用MustOverride 关键字或 abstract关键字进行标示。

下面将前面的CGElement类改成抽象基类。

【VB.NET】

code.vb.net
Public MustInherit Class CGElement
    '此处代码与前面的相同
    Public MustOverride Sub Draw()
End Class

【VC#.NET】

code.vc#.net
public abstract class CGElement
{
    //此处代码与前面的相同"
    public abstract void Draw();
}

将CGElement类改成抽象基类以后,就不能用New关键字或new关键字进行实例化了。在派生类中必须重写抽象方法Draw。下面介绍重写。

重写

重写,或者说覆盖,指的是在派生类中改写从基类中继承来的方法,用另外的方式来实现它,以实现个性。

如果基类的方法为抽象方法,或者是用Overridable(VB.NET中)或virtual(VC#.NET)关键字声明了的可以重写的方法,则在派生类中可以重写该方法。比如在基类CGElement类中有一个可以重写的方法Draw,则派生类CLine类和CRectangle类等中可以重写该方法,分别实现直线段和矩形的绘制。在派生类中重写方法需要使用Overrides关键字或override关键字。

下面接着2.2.3节的例子,在派生类CLine类中重写Draw方法。

【VB.NET】

code.vb.net
Public Overrides Sub Draw()
    text-blue-400">Console..text-blue-400">WriteLine("绘直线段。")
End Sub

【VC#.NET】

code.vc#.net
public override void Draw()
{
    Console.WriteLine("绘制直线段。");
}

遮蔽

使用遮蔽也可以重写基类的方法,但遮蔽不仅仅是重写基类的方法,它还可以替代基类的方法。例如,在VB.ET中,如果基类的方法有两个参数,使用遮蔽,可以在派生类中用带有3个参数的同名方法替代它。另外,使用遮蔽方法重写基类的方法时,不需要在基类中将方法声明为Overridable或virtual。

下面的代码首先创建一个基类CBase,它有一个Add方法,返回两个参数的和:然后创建一个派生类CDerive,.在其中遮蔽基类的Add方法,在两个参数的和的基础上再加上l00以后返回;最后在Forml类中调用CDerive类实例的Add方法。

创建项目以后,首先创建一个CBase类。

【VB.NET】

code.vb.net
Public Class CBase
    Public Function Add(ByVal a As Single,ByVal b As Single)As Single
        Return a+b
    End Function
End Class

【VC#.NET】

code.vc#.net
public class CBase
{
    public CBase(
    {
    }
public float Add(float a,float b)
    {
        return a + b;
    }
}

然后创建CDerive类,代码如下:

【VB.NET】

code.vb.net
Public Class CDerive
    Inherits CBase
    Public Shaows Function Add(ByVal a As Single, ByVal b As Single) As Single
        Return a+b+100
    End Function
End Class

【VC#.NET】

code.vc#.net
public class CDerive:Cbase
{
    public Cderive()
    {
    }
public new float Add(float a, float b)
    {
        retum a+b+100;
    }
}

在Form1类中添加下面的代码,创建一个CDerive类实例sum,然后调用它的Add方法,根拥给定的参数10和20求和。此时基人返回的值将是130,而不是30。

【VB.NET】

code.vb.net
Private Sub Forml_Load(ByVal sender As System.Object,_
        ByVal text-blue-400">e As System.EventArgs) Handles MyBase.Load
    Dim sum As New Cderive()
    sum.Add(10, 20)
End Sub

【VC#.NET】

code.vc#.net
private void Form1_Load(object sender,System.EventArgs e)
{
    CDerive sum=new CDerive();
    sum.Add(10,20);
}

重载

前面在介绍类的时候曾经讲解了重载技术。这里主要介绍在有继承的情况下如何使用重载。如果要重载从基类中继承来的方法,在VB.NET中,必须使用OverLoads关键字。比如,在上面的例子中,在没有遮蔽的情况下,如果要重载基类的Add方法,求两个整型值的和,需要添加下面的代码:

code.vb.net
Public Overloads Function Add(ByVal a As Integer, ByVal b As Integer) As Integer
    Return a+b
End Function

在有遮蔽的情况下重载基类的Add方法,也必须使用Shadows关键字。比如,2.2.5节的例子中,遮蔽基类的Add 方法以后,如果还要重载一个带有3个参数的Add方法,需要添加下面的代码:

code.vb.net
Public Shadows Function Add(ByVal a As Single, ByVal b As Single.
            ByVal c As Single) As Single
    Return a+b+100
End Function

MyBase 和 base

在VB.NET 中使用MyBase或在VC#.NET 中使用base,可以在派生类中调用基类的成员。如2.2.5节遮蔽了基类的Add方法,在派生类中将不能应用该Add方法的定义。但是通过MyBase或base来调用时,仍然可以实现。比如,2.2.5节中遮蔽后重写的Add 方法可以重写为:

【VB.NET】

code.vb.net
puoli Sadows Function Add(ByVal As Single, ByVal b As Single)As Single
    Return MyBase.Add(a, b)+100
End Function

【VC#.NET】

code.vc#.net
public new float Add(float a,float b)
{
    return base.Add(a,b)+100;
}