Visual Basic.NET+SQL Server全程指南
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

实例3-11 隐藏的运用

该实例实现步骤如下。

1 创建模块

在本章的项目chapter3下,右键单击该项目名下面的文件夹“类及对象”,在弹出菜单中选择“添加”→“模块”菜单命令,在弹出的对话框中输入模块名称HideModule,单击“添加”按钮完成模块的创建。

2 添加代码

双击HideModule.vb进入该模块的代码编辑窗口,向其中添加代码3-20中的代码。

代码3-20 HideModule.vb

        01. Imports System
        02. Module HideModule
        03.         Class A '定义一个类A
        04.         Public Overridable Sub F() '定义一个可重写的Sub过程F()
        05.             Console.WriteLine("A.F")
        06.         End Sub
        07.         End Class
        08.         Class B '定义一个类B
        09.         Inherits A '继承类A
        10.         Public Overrides Sub F() '重写类A中的Sub过程F()
        11.             Console.WriteLine("B.F")
        12.         End Sub
        13.         End Class
        14.         Class C '定义一个类C
        15.         Inherits B '继承类B
        16.         Public Overridable Shadows Sub F() '定义可重写并隐藏的Sub过程F()
        17.             Console.WriteLine("C.F")
        18.         End Sub
        19.         End Class
        20.         Class D '定义一个类D
        21.         Inherits C '继承类C
        22.         Public Overrides Sub F() '重写类C中的Sub过程F()
        23.             Console.WriteLine("D.F")
        24.         End Sub
        25.         End Class
        26.         Sub Main()
        27.         Dim d As New D() '实例化类D
        28.         Dim a As A = d '声明一个基类的变量a
        29.         Dim b As B = d '声明一个基类的变量b
        30.         Dim c As C = d '声明一个基类的变量c
        31.         a.F()
        32.         b.F()
        33.         c.F()
        34.         d.F()
        35.     End Sub
        36. End Module

代码3-20执行步骤如下:

① 第1行代码引用System名字空间;

② 第3行到第7行代码定义一个类A,在类A中定义一个可重写的Sub过程F(),用于输出一个字符串“A.F”;

③ 第8行到第13行代码定义一个类B,类B继承类A,在类B中重写类A中的Sub过程F(),实现输出字符串“B.F”;

④ 第14行到第19行代码定义一个类C,类C继承类B,在类C中定义可重写并隐藏的Sub过程F(),实现输出字符串“C.F”;

⑤ 第20行到第25行代码定义一个类D,类D继承类C,在类D中重写类C中的Sub过程F(),实现输出字符串“D.F”;

⑥ 第27行代码实例化类D;第28行代码声明一个基类的变量a,并指定类D的实例扩展到此类;第29行代码声明一个基类的变量b,并指定类D的实例扩展到此类;第30行代码声明一个基类的变量c,并指定类D的实例扩展到此类;

⑦ 在代码3-20中有两个Overridable方法,一个由类A引入,另一个由类C引入。类C引入的方法隐藏从类A继承的方法。这样,类D中的Overrides声明重写类C引入的方法,而且类D不可能重写由类A引入的方法。所以第31行代码调用类B中的过程,第32行代码调用类B中的过程,第33行代码调用类D中的过程,第34行代码调用类D中的过程。

3 运行结果

按F5键,运行程序,结果如图3-12所示。

图3-12 实例3-11运行结果

4.重载、重写和隐藏的异同

重载、重写和隐藏是很容易混淆的类似概念。虽然这三种技术都能创建同名的成员,但它们之间有一些重要的差异。

① 重载的成员用于提供属性或方法的不同版本,这些版本具有相同名称但是接受不同数量或接受不同数据类型的参数。

② 重写的属性和方法用于替换在派生类中不适合的继承的属性或方法。重写的成员必须接受同一数据类型和参数数量。派生类继承重写的成员。

③ 隐藏的成员用于局部替换具有更广范围的成员。任何类型都可隐藏其他类型。例如,可声明隐藏同名继承方法的属性。无法继承隐藏的成员。

3.3.7 构造函数和析构函数

构造函数和析构函数控制对象的创建和销毁。

1.构造函数

为类创建构造函数,就是在类定义的任何位置创建名为Sub New的过程;而要创建参数化构造函数,就是像为其他任何过程指定参数那样为Sub New指定参数的名称和数据类型,如下面的代码所示。

        Sub New(ByVal s As String)

构造函数可被重载,如下面的代码所示。

        Sub New(ByVal s As String, i As Integer)

当定义从另一个类派生的类时,构造函数的第一行必须是对基类构造函数的调用,除非基类有一个可访问的无参数构造函数。例如,对包含以上构造函数的基类的调用为MyBase.New(s)。另外,MyBase.New是可选的,Visual Basic运行库会隐式调用它。

编写了用于调用父对象构造函数的代码后,就可以将任何附加初始化代码添加到Sub New过程。在作为参数化构造函数调用时,Sub New可接受参数,这些参数是从调用构造函数的过程(如Dim AnObject As New ThisClass(X))中传递的。

在VB.NET中处理构造函数时,有以下两点陈述值得注意。

(1)如果类不包含任何实例构造函数声明,则VB.NET提供默认的构造函数;

默认情况下直接调用基类的无参数构造函数。如果基类没有可访问的无参数构造函数,则发生编译期错误。

(2)构造函数不能继承;

陈述(1)包含两个方面的信息。一是如果类型不包含任何实例构造函数声明,则VB.NET提供默认的构造函数,即下面的声明是允许的,在类Shape中没有显式声明任何构造函数,但是程序员仍然可以通过VB.NET自动提供的无参数构造函数New()来实例化类;

        Module Test
            Public Sub Main()
                Dim shape1 As Shape = New Shape
            End Sub
        End Module
        Public Class Shape
            Public Sub Draw()
                'some codes here.
            End Sub
        End Class

二是如果类型中包含有实例构造函数的声明,那么VB.NET不再提供默认的构造函数,例如,下面在类Shape中添加了一个带参数的构造函数。

        Public Class Shape
            Private _pt As Point
            Public Sub New(p As Point)
                _pt = p
            End Sub
            Public Sub Draw()
                'some codes here.
            End Sub
        End Class

那么,如果直接实例化类时,则会产生错误;代码如下:

        Dim shape1 As Shape = New Shape

只能通过调用Shape类中显式声明的带参数的构造函数来实例化类,代码如下:

        Dim shape1 As Shape = New Shape(New Point(0, 0))

因此,如果要在类中同时使用无参数和带参数的构造函数,必须同时显式地声明两种构造函数。代码如下:

        Public Class Shape
            Private _pt As Point
            Public Sub New()
                'some codes here.
            End Sub
            Public Sub New(p As Point)
                _pt = p
            End Sub
            Public Sub Draw()
                'some codes here.
            End Sub
        End Class

陈述(2),构造函数不能继承,即是指,如果要使子类与基类有相同的构造函数,那么必须在每个子类中都显式地声明与基类构造函数相同的构造函数,示例如下:

        Public MustInherit Class Shape
            Private _pt As Point
            Public Sub New()
                'some codes here.
            End Sub
            Public Sub New(p As Point)
                _pt = p
            End Sub
            Public MustOverride Sub Draw()
        End Class
        Public Class LineClass Line
            Inherits Shape
            Public Sub New()
            End Sub
            Public Sub New(p As Point)
                MyBase.New(p)
            End Sub
            Public Overrides Sub Draw()
                'some codes here.
            End Sub
        End Class

这一点非常重要而且也很有意义。因为程序员往往会认为子类继承了基类所有的属性和方法,这可能导致所编的程序出现编译错误。

综合以上两个陈述,在实际编程中应遵循一个原则,即在类中,无论是基类还是子类,都应该显式地声明所有需要的无参数或带参数构造函数。遵循这个原则,既有利于提高编译正确代码的速度,也有利于自己和其他程序员理解代码。

下面以实例说明构造函数的运用。