Shadowing in Visual Basic

Quando due elementi di programmazione condividono lo stesso nome, uno di essi può nascondere l'altro, o eseguirne lo shadowing. In tale situazione, l'elemento nascosto non è disponibile come riferimento. Quando invece il codice usa il nome dell'elemento, il compilatore Visual Basic lo risolve nell'elemento di shadowing.

Scopo

Lo scopo principale dello shadowing è proteggere la definizione dei membri della classe. La classe di base potrebbe subire una modifica che crea un elemento con lo stesso nome di quello già definito. In questo caso, il modificatore Shadows forza la risoluzione dei riferimenti tramite la classe al membro definito, anziché al nuovo elemento della classe di base.

Tipi di shadowing

Un elemento può nascondere un altro elemento in due modi diversi. L'elemento di shadowing può essere dichiarato all'interno di un'area secondaria dell'area contenente l'elemento nascosto, nel qual caso lo shadowing viene eseguito tramite ambito. In alternativa, una classe derivata può ridefinire un membro di una classe di base, nel qual caso lo shadowing viene eseguito tramite ereditarietà.

Shadowing tramite ambito

È possibile che gli elementi di programmazione nello stesso modulo, classe o struttura abbiano lo stesso nome ma un ambito diverso. Quando due elementi vengono dichiarati in questo modo e il codice fa riferimento al nome che condividono, l'elemento con l'ambito più ristretto nasconde l'altro elemento (l'ambito blocco è il più ristretto).

Ad esempio, un modulo può definire una variabile Public denominata tempe una routine all'interno del modulo può dichiarare una variabile locale anch'essa denominata temp. I riferimenti a temp dall'interno della routine accedono alla variabile locale, mentre i riferimenti a temp dall'esterno della routine accedono alla variabile Public. In questo caso, la variabile temp della routine nasconde la variabile temp del modulo.

La figura seguente mostra due variabili, entrambe denominate temp. La variabile locale temp nasconde la variabile membro temp quando vi si accede dall'interno della propria routine p. Tuttavia, la parola chiave MyClass ignora lo shadowing e accede alla variabile membro.

Graphic that shows shadowing through scope.

Per un esempio di shadowing tramite ambito, vedere Procedura: Nascondere una variabile con lo stesso nome di un'altra variabile.

Shadowing tramite ereditarietà

Se una classe derivata ridefinisce un elemento di programmazione ereditato da una classe di base, l'elemento di ridefinizione nasconde l'elemento originale. È possibile nascondere qualsiasi tipo di elemento dichiarato o set di elementi di overload con qualsiasi altro tipo. Ad esempio, una variabile Integer può nascondere una routine Function. Se si nasconde una routine con un'altra routine, è possibile utilizzare un elenco di parametri diverso e un tipo restituito diverso.

La figura seguente illustra una classe di base b e una classe derivata d che eredita da b. La classe di base definisce una routine denominata proc e la classe derivata la nasconde con un'altra routine con lo stesso nome. La prima istruzione Call accede allo shadowing proc nella classe derivata. Tuttavia, la parola chiave MyBase ignora lo shadowing e accede alla routine nascosta nella classe di base.

Graphic diagram of shadowing through inheritance

Per un esempio di shadowing tramite ereditarietà, vedere Procedura: Nascondere una variabile con lo stesso nome di un'altra variabile e Procedura: Nascondere una variabile ereditata.

Shadowing e livello di accesso

L'elemento di shadowing non è sempre accessibile dal codice usando la classe derivata. Ad esempio, potrebbe essere dichiarato Private. In questo caso, lo shadowing viene annullato e il compilatore risolve qualsiasi riferimento allo stesso elemento che avrebbe avuto se non ci fosse stato lo shadowing. Questo elemento è l'elemento accessibile con il minor numero di passaggi derivazionali all'indietro dalla classe di shadowing. Se l'elemento nascosto è una routine, la risoluzione corrisponde alla versione accessibile più vicina con lo stesso nome, elenco di parametri e tipo restituito.

Nell'esempio seguente viene illustrata una gerarchia di ereditarietà di tre classi. Ogni classe definisce una routine Subdisplay e ogni classe derivata nasconde la routine display nella relativa classe di 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  

Nell'esempio precedente la classe derivata secondClass nasconde display con una routine Private. Quando il modulo callDisplay chiama display in secondClass, il codice chiamante si trova al di fuori di secondClass e pertanto non può accedere alla routine privata display. Lo shadowing viene annullato e il compilatore risolve il riferimento alla routine display della classe di base.

Tuttavia, l'ulteriore classe derivata thirdClass dichiara display come Public, quindi il codice in callDisplay può accedervi.

Shadowing e override

Non bisogna confondere lo shadowing con l'override. Entrambi vengono usati quando una classe derivata eredita da una classe di base ed entrambi ridefiniscono un elemento dichiarato con un altro. Esistono tuttavia differenze significative tra i due. Per un confronto, vedere Differenze tra shadowing e override.

Shadowing e overload

Se si nasconde lo stesso elemento della classe di base con più di un elemento nella classe derivata, gli elementi di shadowing diventano versioni di overload di tale elemento. Per altre informazioni, vedere Procedure Overloading.

Accesso a un elemento nascosto

Quando si accede a un elemento da una classe derivata, in genere si esegue questa operazione tramite l'istanza corrente di tale classe derivata qualificando il nome dell'elemento con la parola chiave Me. Se la classe derivata nasconde l'elemento nella classe di base, è possibile accedere all'elemento della classe di base qualificandolo con la parola chiave MyBase.

Per un esempio di accesso a un elemento nascosto, vedere Procedura: Accedere a una variabile nascosta da una classe derivata.

Dichiarazione della variabile oggetto

La modalità di creazione della variabile oggetto può influire anche sul fatto che la classe derivata acceda a un elemento di shadowing o all'elemento nascosto. Nell'esempio seguente vengono creati due oggetti da una classe derivata, ma un oggetto viene dichiarato come classe di base e l'altro come classe derivata.

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  

Nell'esempio precedente la variabile basObj viene dichiarata come classe di base. L'assegnazione di un oggetto dervCls a tale variabile costituisce una conversione che supporta un maggior numero di dati ed è pertanto valida. Tuttavia, la classe di base non può accedere alla versione shadowing della variabile z nella classe derivata, quindi il compilatore risolve basObj.z nel valore origine della classe di base.

Vedi anche