Visual Basic 中的隐藏

当两个编程元素共享相同的名称时,其中一个可以隐藏或阴影化另一个。 在这种情况下,阴影化的元素不可用于引用;相反,当代码使用元素名称时,Visual Basic 编译器会将其解析为阴影元素。

目的

阴影的主要目的是保护类成员的定义。 基类可能会发生更改,该更改将创建一个与你已定义的元素同名的元素。 如果发生这种情况,Shadows 修饰符会强制将通过你的类的引用解析为你定义的成员,而不是新的基类元素。

阴影类型

一个元素可以通过两种不同的方式对另一个元素进行阴影处理。 可以在包含阴影化元素的区域的子区域内声明阴影元素,在这种情况下,阴影处理是通过范围实现的。 或者,派生类可以重新定义基类的成员,在这种情况下,阴影处理是通过继承完成的。

通过范围实现阴影

同一模块、类或结构中的编程元素可以具有相同的名称,但范围不同。 如果以这种方式声明两个元素,并且代码引用了它们共享的名称,则范围较窄的元素将对另一个元素进行阴影处理(块范围是最窄的范围)。

例如,模块可以定义一个名为 tempPublic 变量,并且该模块中的一个过程可以声明同样名为 temp 的局部变量。 在该过程中引用 temp 将访问局部变量,而从该过程外部引用 temp 将访问 Public 变量。 在这种情况下,过程变量 temp 对模块变量 temp 进行了阴影处理。

下图显示了两个名为 temp 的变量。 局部变量 temp 在从其自己的过程 p 中被访问时对成员变量 temp 进行阴影处理。 不过,MyClass 关键字将绕过阴影并访问该成员变量。

Graphic that shows shadowing through scope.

有关通过范围实现阴影的示例,请参阅如何:隐藏与你的变量同名的变量

通过继承实现阴影。

如果派生类重新定义了从基类继承的编程元素,则重新定义的元素将对原始元素进行阴影处理。 可以用任何其他类型对任何类型的已声明元素或重载元素集进行阴影处理。 例如,Integer 变量可以对 Function 过程进行阴影处理。 如果使用另一个过程来对一个过程进行阴影处理,可以使用不同的参数列表和不同的返回类型。

下图显示了基类 b 以及继承自 b 的派生类 d。 基类定义了一个名为 proc 的过程,派生类将使用另一个同名的过程来对该过程进行阴影处理。 第一条 Call 语句将访问派生类中的阴影 proc。 不过,MyBase 关键字将绕过阴影并访问基类中的阴影化过程。

Graphic diagram of shadowing through inheritance

有关通过继承实现阴影的示例,请参阅如何:隐藏与你的变量同名的变量以及如何:隐藏继承的变量

阴影和访问级别

不能始终使用派生类从代码访问阴影元素。 例如,它可能声明为 Private。 在这种情况下,阴影会失效,并且如果没有阴影,编译器将解析对它所有的同一元素的任何引用。 此元素是可访问的元素,通过从阴影类向后执行最少的派生步骤即可访问。 如果阴影化元素是一个过程,则将解析为具有相同名称、参数列表和返回类型的最接近的可访问版本。

下面的示例显示了三个类的继承层次结构。 每个类都定义一个 Sub 过程 display,并且每个派生类都会在其基类中对 display 过程进行阴影处理。

Public Class firstClass  
    Public Sub display()  
        MsgBox("This is firstClass")  
    End Sub  
End Class  
Public Class secondClass  
    Inherits firstClass  
    Private Shadows Sub display()  
        MsgBox("This is secondClass")  
    End Sub  
End Class  
Public Class thirdClass  
    Inherits secondClass  
    Public Shadows Sub display()  
        MsgBox("This is thirdClass")  
    End Sub  
End Class  
Module callDisplay  
    Dim first As New firstClass  
    Dim second As New secondClass  
    Dim third As New thirdClass  
    Public Sub callDisplayProcedures()  
        ' The following statement displays "This is firstClass".  
        first.display()  
        ' The following statement displays "This is firstClass".  
        second.display()  
        ' The following statement displays "This is thirdClass".  
        third.display()  
    End Sub  
End Module  

在前面的示例中,派生类 secondClass 通过 Private 过程对 display 进行阴影处理。 当模块 callDisplaysecondClass 中调用 display 时,调用代码位于 secondClass 外部,因此,无法访问私有 display 过程。 阴影已失效,编译器将引用解析为基类 display 过程。

但是,进一步派生的类 thirdClassdisplay 声明为 Public,因此 callDisplay 中的代码可以访问它。

阴影和重写。

不要将阴影与重写混淆。 当派生类从基类继承时会使用这两种方法,并且它们都将一个已声明的元素重新定义为另一个。 但两者之间存在显著差异。 有关比较,请参阅阴影和重写之间的差异

阴影和重载

如果在派生类中对具有多个元素的同一个基类元素进行阴影处理,则阴影元素将成为该元素的重载版本。 有关更多信息,请参见 Procedure Overloading

访问阴影化元素

从派生类访问某个元素时,通常通过该派生类的当前实例来执行此操作,方法是使用 Me 关键字限定元素名称。 如果派生类对基类中的元素进行阴影处理,则可以通过使用 MyBase 关键字限定基类元素来访问它。

有关访问阴影化元素的示例,请参阅如何:访问被派生类隐藏的变量

对象变量的声明

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

Public Class baseCls  
    ' The following statement declares the element that is to be shadowed.  
    Public z As Integer = 100  
End Class  
Public Class dervCls  
    Inherits baseCls  
    ' The following statement declares the shadowing element.  
    Public Shadows z As String = "*"  
End Class  
Public Class useClasses  
    ' The following statement creates the object declared as the base class.  
    Dim basObj As baseCls = New dervCls()  
    ' Note that dervCls widens to its base class baseCls.  
    ' The following statement creates the object declared as the derived class.  
    Dim derObj As dervCls = New dervCls()  
    Public Sub showZ()
    ' The following statement outputs 100 (the shadowed element).  
        MsgBox("Accessed through base class: " & basObj.z)  
    ' The following statement outputs "*" (the shadowing element).  
        MsgBox("Accessed through derived class: " & derObj.z)  
    End Sub  
End Class  

在前面的示例中,变量 basObj 声明为基类。 为它分配一个 dervCls 对象将形成扩大转换,因此,这是有效的。 但是,基类无法访问派生类中的变量 z 的阴影版本,因此编译器将 basObj.z 解析为原始基类值。

另请参阅