Sombrear en Visual Basic

Cuando dos elementos de programación comparten el mismo nombre, uno de ellos puede ocultar, o sombrear, al otro. En esta situación, el elemento sombreado no está disponible para referencia; en su lugar, cuando el código usa el nombre del elemento, el compilador de Visual Basic lo resuelve en el elemento que realiza el sombreado.

Propósito

El objetivo principal del sombreado es proteger la definición de los miembros de la clase. La clase base puede sufrir un cambio que crea un elemento con el mismo nombre que el que ya ha definido. Si esto sucede, el modificador Shadows obliga a que las referencias a través de la clase se resuelvan en el miembro definido, en lugar de en el nuevo elemento de la clase base.

Tipos de sombreado

Un elemento puede sombrear a otro elemento de dos maneras diferentes. El elemento que realiza el sombreado se puede declarar dentro de una subdivisión de la región que contiene el elemento sombreado, en cuyo caso el sombreado se realiza mediante el ámbito. O bien, una clase derivada puede redefinir un miembro de una clase base, en cuyo caso el sombreado se realiza mediante herencia.

Sombreado mediante el ámbito

Es posible que los elementos de programación de un mismo módulo, clase o estructura tengan el mismo nombre, pero un ámbito diferente. Cuando se declaran dos elementos de esta manera y el código hace referencia al nombre que comparten, el elemento con el ámbito más limitado sombrea al otro elemento (el ámbito de bloque es el más limitado).

Por ejemplo, un módulo puede definir una variable Public denominada temp y un procedimiento dentro del módulo puede declarar una variable local también denominada temp. Las referencias a temp desde dentro del procedimiento acceden a la variable local, mientras que las referencias a temp desde fuera del procedimiento acceden a la variable Public. En este caso, la variable de procedimiento temp sombrea a la variable de módulo temp.

En la ilustración siguiente se muestran dos variables, ambas denominadas temp. La variable local temp sombre a la variable de miembro temp cuando se accede desde su propio procedimiento p. Sin embargo, la palabra clave MyClass omite el sombreado y accede a la variable de miembro.

Graphic that shows shadowing through scope.

Para obtener un ejemplo de sombreado mediante el ámbito, consulte Cómo: Ocultar una variable con el mismo nombre que su variable.

Sombreado mediante herencia

Si una clase derivada vuelve a definir un elemento de programación heredado de una clase base, el elemento redefinido sombrea al elemento original. Puede sombrear cualquier tipo de elemento declarado, o conjunto de elementos sobrecargados, con cualquier otro tipo. Por ejemplo, una variable Integer puede sombrear un procedimiento Function. Si sombre un procedimiento con otro procedimiento, puede usar una lista de parámetros diferente y otro tipo de valor devuelto.

En la ilustración siguiente se muestra una clase base b y una clase derivada d que se hereda de b. La clase base define un procedimiento denominado proc y la clase derivada lo sombrea con otro procedimiento del mismo nombre. La primera instrucción Call accede al procedimiento proc que realiza el sombreado en la clase derivada. Sin embargo, la palabra clave MyBase omite este procedimiento y accede al procedimiento sombreado en la clase base.

Graphic diagram of shadowing through inheritance

Para obtener un ejemplo de sombreado mediante herencia, consulte Cómo: Ocultar una variable con el mismo nombre que su variable y Cómo: Ocultar una variable heredada.

Sombreado y nivel de acceso

El elemento que realiza el sombreado no siempre es accesible desde el código mediante la clase derivada. Por ejemplo, podría declararse Private. En tal caso, el sombreado se anula y el compilador resuelve cualquier referencia al mismo elemento como lo haría si no existiese el sombreado. Este elemento es el elemento accesible con menos pasos de derivación hacia atrás desde la clase que realiza el sombreado. Si el elemento sombreado es un procedimiento, la resolución es a la versión accesible más cercana con el mismo nombre, lista de parámetros y tipo de valor devuelto.

En el ejemplo siguiente se muestra una jerarquía de herencia de tres clases. Cada clase define un Subprocedimiento display y cada clase derivada sombrea el procedimiento display de su clase base.

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  

En el ejemplo anterior, la clase derivada secondClass sombrea display por un procedimiento Private. Cuando el módulo callDisplay llama a display en secondClass, el código de llamada está fuera de secondClass y, por lo tanto, no puede acceder al procedimiento privado display. El sombreado se anula y el compilador resuelve la referencia al procedimiento display de la clase base.

Sin embargo, la clase derivada thirdClass adicional declara display como Public, por lo que el código de callDisplay puede acceder a él.

Sombreado y reemplazos

No confunda el sombreado con el reemplazo. Ambas funciones se utilizan cuando una clase derivada se hereda de una clase base, y ambas redefinen un elemento declarado con otro. Pero existen diferencias significativas entre las dos. Para ver una comparación, consulte Diferencias entre sombrear y reemplazar.

Sombreado y sobrecarga.

Si sombrea el mismo elemento de la clase base con más de un elemento de la clase derivada, los elementos de sombreado se transforman en versiones sobrecargadas de ese elemento. Para obtener más información, consulta Procedure Overloading.

Acceso a un elemento sombreado

Cuando accede a un elemento de una clase derivada, normalmente lo hace a través de la instancia actual de esa clase derivada, habilitando el nombre del elemento con la palabra clave Me. Si la clase derivada sombrea el elemento de la clase base, para acceder al elemento de la clase base puede habilitarlo con la palabra clave MyBase.

Para obtener un ejemplo de acceso a un elemento sombreado, consulte Cómo: Obtener acceso a una variable que oculta una clase derivada.

Declaración de la variable de objeto

La forma en que se crea la variable de objeto también puede afectar a si la clase derivada accede al elemento que sombrea o al que es sombreado. En el ejemplo siguiente se crean dos objetos a partir de una clase derivada, pero un objeto se declara como la clase base y el otro como la clase derivada.

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  

En el ejemplo anterior, la variable basObj se declara como la clase base. Asignarle un objeto dervCls a ella constituye una conversión de ampliación y, por tanto, es válido. Sin embargo, la clase base no puede acceder a la versión que efectúa el sombreado de la variable z en la clase derivada, por lo que el compilador resuelve basObj.z en el valor de la clase base original.

Consulte también