实例3-10 重写的运用
该实例实现步骤如下。
1 创建模块
在本章的项目chapter3下,右键单击该项目名下面的文件夹“类及对象”,在弹出菜单中选择“添加”→“模块”菜单命令,在弹出的对话框中输入模块名称RewriteModule,单击“添加”按钮完成模块的创建。
2 添加代码
双击RewriteModule.vb进入该模块的代码编辑窗口,向其中添加代码3-19中的代码。
代码3-19 RewriteModule.vb
01. Module RewriteModule 02. Class Basicclass 03. Overridable Function basic(ByVal str As String) '定义一个允许重写 '的方法 04. Console.WriteLine("基类中的字符串" + str) 05. End Function 06. End Class 07. Class InBasicclass 08. Inherits Basicclass '继承Basicclass类 09. Overrides Function basic(ByVal str As String) '重写基类中的方法 10. Console.WriteLine(MyBase.basic(str) & "继承类中的字符串" & str) 11. End Function 12. End Class 13. Sub main() 14. Dim bcl As Basicclass = New Basicclass '实例化基类 15. Dim inbcl As New InBasicclass '实例化派生类 16. bcl.basic("Rewrite") '分别调用两个类中的方法 17. inbcl.basic("Rewrite") 18. End Sub 19. End Module
代码3-19执行步骤如下:
① 第2行代码定义一个类Basicclass;
② 第3行代码在Basicclass类中定义一个允许重写的方法basic()。第4行代码输出basic()参数值;
③ 第7行和第8行代码定义InBasicclass类继承Basicclass类;
④ 第9行到第11行代码重写基类Basicclass中的方法basic();
⑤ 第14行代码实例化基类,第15行代码实例化派生类;
⑥ 第16和第17行代码分别调用两个类中的方法。
3 运行结果
按F5键,运行程序,结果如图3-11所示。
图3-11 实例3-10运行结果
3.隐藏
当两个编程元素共享同一个名称时,其中的一个可能会掩藏,或者说“隐藏”。在这种情况下,隐藏的元素不能引用,相反,代码将使用共享的名称,Visual Basic编译器解析代码以隐藏元素。
一个元素可以以两种不同的方式隐藏另一元素。可以在包含隐藏元素的区域的子域内声明隐藏元素,这种情况下,隐藏是通过范围来完成的;或者一个派生类可以定义一个基类成员,这种情况下隐藏是通过继承来完成的。
(1)通过范围进行隐藏
同一模块、类或结构中的编程元素可以名称相同但范围不同。当以这种方式声明了两个元素,并且代码引用了它们共享的名称时,范围较窄的元素将隐藏其他元素(块范围是最窄的)。
例如,模块可以定义一个名为Temp的Public变量,并且模块内的过程可以声明一个名称同样是Temp的局部变量。在过程内引用Temp将访问局部变量,而在过程外引用Temp则访问Public变量。这种情况下,过程变量Temp隐藏模块变量Temp。
模块变量被局部变量隐藏时,通过用模块名、类或声明该模块变量的结构来限定该变量,仍可在过程内访问它。示例如下:
Module Module1 Public Num As Integer ' Num全局变量,作用于整个Module1模块中 Sub Show() Dim Num As Integer ' Num局部变量 Num = 2 ' 设置局部变量Num的值为2 MsgBox(CStr(Module1.Num)) ' 使用弹出对话框显示全局变量Num的值 End Sub Sub UseModuleLevelNum() Num = 1 ' 设置全局变量Num的值 ' ... Show() '调用Show()方法, 显示全局变量Num的值 End Sub End Module
(2)通过继承隐藏
如果一个派生类重新定义了一个继承基类的编程元素,重定义的元素将隐藏原始元素。可以用任何其他类型的元素隐藏任意类型的已声明元素或一组重载元素。例如,一个Integer变量可以隐藏一个Function过程。如果用另外一个过程隐藏某一过程,可以使用一个不同的参数列表以及一个不同的返回类型。
注意
如果隐藏元素在使用代码内不可访问(例如,如果将其声明为Private),隐藏将会失败,而编译器将解析对同一元素的所有引用,就像没有隐藏一样。这时自隐藏类向后追溯,派生步骤最少的可访问元素。如果被隐藏的元素是过程,将解析到具有相同的名称、参数列表和返回类型的最近似的可访问版本。
不要将隐藏与重写混淆。二者都在派生类继承基类时使用,并且都是用另外的元素重定义一个已声明的元素。但是二者之间有区别,表3-1对隐藏和重写进行了比较。
表3-1 隐藏和重写比较
表3-1中标号①②说明如下:
① 调用序列包括元素类型(Function、Sub或Property)、名称、参数列表和返回类型。不能用属性重写过程,或者用过程重写属性。不能用一个Sub过程重写一个Function过程,反之亦然。
② 若隐藏元素在后来的派生类中不可访问,则没有继承隐藏。例如,如果声明隐藏元素为Private,则派生类的继承类就会继承原始元素,而不是隐藏元素。
当访问导出类中的元素时,通常要通过该导出类的当前实例,并用Me关键字限定元素名。如果导出类隐藏了基类中的元素,则可通过用MyBase关键字限定基类元素来访问它的基类。
访问派生类的方式能影响到代码是使用隐藏元素还是它所隐藏的元素。在下面的示例中,变量BObj被声明为基类,为它指定一个DervCls()对象将继续一个扩展转换,这是有效的。但是,基类不能访问派生类中变量Z的隐藏版本,因此编译器会将BObj.Z解析为原始的基类值。
Public Class BaseCls Public Z As Integer = 100 '定义公共变量Z End Class Public Class DervCls Inherits BaseCls Public Shadows Z As String = "*" ' 隐藏基类BaseCls的公共变量Z End Class Public Class UseClasses Dim BObj As BaseCls = New DervCls() ' DervCls扩展到基类BaseCls Dim DObj As DervCls = New DervCls() ' 实例化派生类DervCls Public Sub ShowZ() MsgBox("Accessed through base class: " & BObj.Z) ' 显示Z值为 100 MsgBox("Accessed through derived class: " & DObj.Z) '显示Z值为"*" End Sub End Class
注意
如果在派生类中用多个元素隐藏同一个基类元素,隐藏元素将变为重载版本。
也可这样理解,派生类型通过重新声明继承的类型成员来隐藏该成员的名称。隐藏一个名称并不意味着移除具有该名称的继承类型成员;而只是使所有具有该名称的继承类型成员在派生类中都不可用。隐藏声明可以是任何类型的实体。
可以重载的实体,有两种形式的隐藏。一是“按名称隐藏”,它是用Shadows关键字指定的。按名称隐藏的实体可以按该名称隐藏基类中的一切内容,包括所有重载。二是“按名称和签名隐藏”,它是用Overloads关键字指定的。按名称和签名隐藏的实体可以按该名称隐藏与实体具有相同签名的一切内容。示例如下:
Imports System Class Base Sub F() End Sub Sub F(ByVal i As Integer) End Sub Sub G() End Sub Sub G(ByVal i As Integer) End Sub End Class Class Derived Inherits Base '仅隐藏F(ByVal i As Integer). Overloads Sub F(ByVal i As Integer) End Sub ' 隐藏G()和G(Integer). Shadows Sub G(ByVal i As Integer) End Sub End Class Module Test Sub Main() Dim x As Derived = New Derived() x.F() ' 调用基类中的F() x.G() ' 产生错误: No such method End Sub End Module
未指定Shadows或Overloads的隐藏方法或属性假定为Shadows。如果一组重载实体的一个成员指定了Shadows或Overloads关键字,则所有成员都必须指定该关键字。不能同时指定Shadows和Overloads关键字。
下面以实例说明隐藏的运用。