重载(Overloads)、重写(Overrides)和隐藏(Shadows) 的区别及用法

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

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

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

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

一、重载属性和方法

  重载是在一个类中用相同的名称但是不同的参数类型创建一个以上的过程、实例构造函数或属性。

1、重载用法

  当对象模型指示对于在不同数据类型上进行操作的过程使用同样名称时,重载非常有用。例如,可显示几种不同数据类型的类可以具有类似如下所示 Display 过程:

?
Overloads  Sub  Display( ByVal  theChar As  Char )
     ' Add code that displays Char data.
End  Sub
Overloads  Sub  Display( ByVal  theInteger As  Integer )
     ' Add code that displays Integer data.
End  Sub
Overloads  Sub  Display( ByVal  theDouble As  Double )
     ' Add code that displays Double data.
End  Sub

如果不使用重载,那么即使每个过程执行相同的操作,也需要为它们创建不同的名称,如下所示:

?
Sub  DisplayChar( ByVal  theChar As  Char )
     ' Add code that displays Char data.
End  Sub
Sub  DisplayInt( ByVal  theInteger As  Integer )
     ' Add code that displays Integer data.
End  Sub
Sub  DisplayDouble( ByVal  theDouble As  Double )
     ' Add code that displays Double data.
End  Sub

因为重载提供了对可用数据类型的选择,所以它使得属性或方法的使用更为容易。例如,可以用下列任一代码行调用前面讨论过的重载 Display 方法:

?
' Call Display with a literal of type Char.
Display( "9" c)
' Call Display with a literal of type Integer.
Display(9)
' Call Display with a literal of type Double.
Display(9.9R)

在运行时,Visual Basic 根据指定参数的数据类型来调用正确的过程。

2、重载规则

用同样名称添加两个或更多属性或方法可以创建类的一个重载成员。除了重载派生成员,每一个重载成员必须具有不同的参数列表。当重载属性或过程时,下面的项不能用作区分特征:

  • 应用于成员或成员参数的修饰符,如 ByVal 或 ByRef。

  • 参数名

  • 过程的返回类型

  重载时关键字 Overloads 是可选的,但如果任一重载成员使用了该 Overloads 关键字,则其他所有同名重载成员也必须指定该关键字。

  派生类可以用具有相同参数和参数类型的成员重载继承成员,该过程称为“按名称和签名隐藏”。如果按名称和签名隐藏时使用了 Overloads 关键字,将使用成员的派生类实现而不是基类中的实现,并且该成员的所有其他重载对于派生类的实例都可用。

  如果用一个具有相同参数和参数类型的成员重载继承成员时省略 Overloads 关键字,则该重载称为“按名称隐藏”。按名称隐藏替换成员的继承实现,使所有其他重载对于派生类及由其派生的类的实例都不可用。

  Overloads 和 Shadows 修饰符不能同时被同一个属性或方法所使用。

二、重写属性和方法

  派生类继承在其基类中定义的属性和方法。因为您可以在这些项适合于派生类时重新使用它们,所以这非常有用。如果基类中的属性或方法用 Overridable 关键字进行标记,则您可以为派生类中的成员定义新实现。使用 Overrides 关键字可以隐藏成员,方法为:在派生类中重定义该成员。当无法“按原状”使用成员时,这很有用。

  实际上,重写的成员经常用于实现多态性。下列规则适用于重写方法。

  • 仅可重写在其基类中用 Overridable 关键字进行标记的成员。

  • 默认情况下,属性和方法为 NotOverridable。

  • 重写的成员必须具有与从基类继承的成员相同的参数。

  • 成员的新实现可通过在方法名称前指定 MyBase 来调用父类中的原始实现。

示例

  假设您要定义类以处理工资单。您可以定义一个一般 Payroll 类,其中包含计算普通周工资单的 RunPayroll 方法。然后可将 Payroll 用作更专用的 BonusPayroll 类(分发雇员奖金时可能会使用该类)的基类。

  BonusPayroll 类可继承并重写在基类 Payroll 中定义的 PayEmployee 方法。

  下面的示例定义基类 Payroll 和派生类 BonusPayroll,该派生类重写继承方法 PayEmployee。过程 RunPayroll, 创建 Payroll 对象和 BonusPayroll 对象,然后将其传递给函数 Pay,该函数为这两个对象执行 PayEmployee方法。

复制代码
 1 Const BonusRate As Decimal = 1.45D
 2  Const PayRate As Decimal = 14.75D
 3 
 4 Class Payroll
 5     Overridable Function PayEmployee( _
 6         ByVal HoursWorked As Decimal, _
 7         ByVal PayRate As Decimal) _
 8         As Decimal
 9 
10         PayEmployee = HoursWorked * PayRate
11     End Function
12 End Class
13 
14 Class BonusPayroll
15     Inherits Payroll
16     Overrides Function PayEmployee( _
17         ByVal HoursWorked As Decimal, _
18         ByVal PayRate As Decimal) _
19         As Decimal
20 
21         ' The following code calls the original method in the base 
22         ' class, and then modifies the returned value.
23         PayEmployee = MyBase.PayEmployee(HoursWorked, PayRate) * BonusRate
24     End Function
25 End Class
26 
27 Sub RunPayroll()
28     Dim PayrollItem As Payroll = New Payroll
29     Dim BonusPayrollItem As New BonusPayroll
30     Dim HoursWorked As Decimal = 40
31 
32     MsgBox("Normal pay is: " & _
33         PayrollItem.PayEmployee(HoursWorked, PayRate))
34     MsgBox("Pay with bonus is: " & _
35         BonusPayrollItem.PayEmployee(HoursWorked, PayRate))
36 End Sub
复制代码

三、Visual Basic 中的阴影操作

  当两个编程元素共享同一个名称时,其中一个元素可能会掩藏(或者说“隐藏”)另一个元素。在这种情况下,被隐藏的元素不能引用;相反,当您的代码使用该元素名时,Visual Basic 编译器会将其解析为隐藏元素。

1、用途

  隐藏的主要目的是保护类成员的定义。基类可能会经历这样的变化:用您已经定义的相同名称创建元素。如果发生这种变化,Shadows 修饰符就会通过您的类强制引用被解析为您定义的成员,而不是解析为新的基类元素。

2、隐藏类型

  一个元素可以以两种不同的方式隐藏另一元素。可以在包含被隐藏元素的区域的子区域内声明隐藏元素,这种情况下,隐藏是“通过范围”来完成的。或者一个派生类可以重新定义一个基类成员,这种情况下,隐藏是“通过继承”来完成的。

通过范围进行隐藏

同一模块、类或结构中的编程元素可以名称相同但范围不同。当以这种方式声明了两个元素,并且代码引用了它们共享的名称时,范围较窄的元素将隐藏其他元素(块范围是最窄的)。

例如,模块可以定义一个名为 temp 的 Public 变量,并且该模块内的过程可以声明一个名称同样为 temp 的局部变量。从过程内引用 temp 将访问局部变量,而从过程外引用 temp 访问 Public 变量。这种情况下,过程变量 temp隐藏模块变量 temp

下图显示了两个变量,这两个变量的名称都是 temp。当从局部变量自身的过程 p 内访问时,该局部变量 temp 隐藏了成员变量 temp。不过,MyClass 关键字会绕开该隐藏并访问该成员变量。

通过范围进行隐藏

 
通过继承隐藏

如果一个派生类重新定义了一个继承基类的编程元素,重定义的元素将隐藏原始元素。可以用任何其他类型的元素隐藏任意类型的已声明元素或一组重载元素。例如,一个 Integer 变量可以隐藏一个 Function 过程。如果用一个过程隐藏另一个过程,可以使用不同的参数列表和不同的返回类型。

下图显示的是基类 b 和从 b 继承的派生类 d。该基类定义一个名为 proc 的过程,然后派生类用另一个同名过程隐藏该过程。第一个 Call 语句访问派生类中隐藏的 proc。不过,MyBase 关键字会绕开该隐藏并访问基类中隐藏的过程。

通过继承隐藏

 
3、隐藏和访问级别

从使用派生类的代码中并不总是能够访问隐藏元素。例如,它可能被声明为 Private。在这种情况下,隐藏就会失败,并且如果以前没有隐藏,编译器就会将任何引用解析为它包含的同一个元素。这是自隐藏类向后追溯时派生步骤最少的可访问元素。如果被隐藏的元素是一个过程,则将解析为具有相同的名称、参数列表和返回类型的最近似的可访问版本。

下面的示例显示了三个类的继承层次结构。每个类都定义一个 Sub 过程 display,并且每个派生类都隐藏其基类中的 display 过程。

复制代码
 1 Public Class firstClass
 2     Public Sub display()
 3         MsgBox("This is firstClass")
 4     End Sub
 5 End Class
 6 Public Class secondClass
 7     Inherits firstClass
 8     Private Shadows Sub display()
 9         MsgBox("This is secondClass")
10     End Sub
11 End Class
12 Public Class thirdClass
13     Inherits secondClass
14     Public Shadows Sub display()
15         MsgBox("This is thirdClass")
16     End Sub
17 End Class
18 Module callDisplay
19     Dim first As New firstClass
20     Dim second As New secondClass
21     Dim third As New thirdClass
22     Public Sub callDisplayProcedures()
23         ' The following statement displays "This is firstClass".
24         first.display()
25         ' The following statement displays "This is firstClass".
26         second.display()
27         ' The following statement displays "This is thirdClass".
28         third.display()
29     End Sub
30 End Module
复制代码
在前一个示例中,派生类  secondClass 用一个 Private 过程隐藏  display。模块  callDisplay 调用  secondClass 中的  display 时,调用代码在  secondClass 外部,因此不能访问私有的  display 过程。隐藏失败,并且编译器将引用解析为基类  display 过程。

  不过,进一步派生的类 thirdClass 将 display 声明为 Public,这样 callDisplay 中的代码就能访问它。

4、访问被隐藏的元素

当访问导出类中的元素时,通常要通过该导出类的当前实例,并用 Me 关键字限定元素名。如果导出类隐藏了基类中的元素,则可以通过用 MyBase 关键字限定基类元素来访问它。

 对象变量声明

创建对象变量的方式也会影响派生类是访问隐藏元素还是被隐藏的元素。下面的示例从一个派生类创建两个对象,但一个对象被声明为基类,另一个被声明为派生类。

复制代码
 1 Public Class baseCls
 2     ' The following statement declares the element that is to be shadowed.
 3     Public z As Integer = 100
 4 End Class
 5 Public Class dervCls
 6     Inherits baseCls
 7     ' The following statement declares the shadowing element.
 8     Public Shadows z As String = "*"
 9 End Class
10 Public Class useClasses
11     ' The following statement creates the object declared as the base class.
12     Dim basObj As baseCls = New dervCls()
13     ' Note that dervCls widens to its base class baseCls.
14     ' The following statement creates the object declared as the derived class.
15     Dim derObj As dervCls = New dervCls()
16     Public Sub showZ() 
17     ' The following statement outputs 100 (the shadowed element).
18         MsgBox("Accessed through base class: " & basObj.z)
19     ' The following statement outputs "*" (the shadowing element).
20         MsgBox("Accessed through derived class: " & derObj.z)
21     End Sub
22 End Class
复制代码

在前面的示例中,变量 basObj 被声明为基类。为它指定一个 dervCls 对象将继续一个扩展转换,因而是有效的。但是,基类不能访问派生类中变量 z 的隐藏版本,因此编译器会将 basObj.z 解析为原始的基类值

原文引用地址:http://hly973.blog.sohu.com/8606313.html

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值