“Ombre” e “Sovrascrivi” in VB.NET

Qual è il significato delle due parole chiave Ombre e sostituzioni ? Che cosa fanno e per quale contesto è preferibile l’uno o l’altro?

Non considererei Shadows un vero concetto di OOP. Le sostituzioni indicano che stai fornendo funzionalità nuove o aggiuntive per un metodo / proprietà ecc. Che è stato dichiarato in una class antenata. Shadows in realtà induce il compilatore a pensare che il metodo / proprietà genitore ecc non esiste nemmeno.

Non mi serve per Shadows. Attenersi alle sostituzioni. Questi tipi di utili “caratteristiche” che VB ha fornito per anni finiscono sempre per causare dolore a un certo punto.

Overrides è il qualificatore più normale. Se la class figlio ridefinisce una funzione di class base in questo modo, indipendentemente da come viene fatto riferimento a un object figlio (utilizzando una class base o un riferimento class figlio), si tratta della funzione figlio chiamata.

D’altra parte, se la funzione class figlio ombreggia la funzione della class base, quindi un object figlio a cui si accede tramite un riferimento di class base utilizzerà tale funzione della class base, nonostante sia un object figlio.
La definizione della funzione figlio viene utilizzata solo se si accede all’object figlio utilizzando un riferimento figlio corrispondente.

Shadowing probabilmente non fa quello che pensi che faccia.

Considera le seguenti classi:

Public MustInherit Class A Public Function fX() As Integer Return 0 End Function End Class Public Class B Inherits A Public Shadows Function fX() As Integer Return 1 End Function End Class 

Ora li uso:

 Dim oA As A Dim oB As New B oA = oB 

Probabilmente pensi che oA e oB siano uguali?

No.

oA.fx = 0 mentre oB.fx = 1

Questo è un comportamento molto pericoloso ed è appena citato nei documenti.

Se avessi usato l’override, sarebbero stati gli stessi.

Così mentre ci sono usi legittimi per le ombre, è probabile che qualunque cosa tu stia facendo non è una di queste e dovrebbe essere evitata.

Sostituzioni: estensione o creazione di funzionalità alternative per un metodo.

Esempio: aggiungere o estendere la funzionalità dell’evento Paint di una finestra.

 Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs) MyBase.OnPaint(e) ' retain the base class functionality 'add code for extended functionality here End Sub 

Ombre – Ridefinisce un metodo ereditato e ne forza l’utilizzo per tutte le classi istanziate con quel tipo. In altre parole, il metodo non è sovraccarico ma ridefinito e i metodi della class base non sono disponibili, forzando così l’uso della funzione dichiarata nella class. Shadows conserva o mantiene la definizione del metodo in modo tale da non essere distrutta se i metodi della class base vengono modificati.

Esempio: imponi a tutte le classi “B” di usare la sua strana aggiunta di una definizione tale che se i metodi di class A Aggiungi vengono modificati non influenzerà l’aggiunta di B. (Nasconde tutti i metodi “Aggiungi” della class base. Non sarà in grado di chiamare A.Add (x, y, z) da un’istanza di B.)

 Public Class A Public Function Add(ByVal x As Integer, ByVal y As Integer) As Integer Return x + y End Function Public Function Add(ByVal x As Integer, ByVal y As Integer, ByVal z As Integer) As Integer Return x + y + z End Function End Class Public Class B Inherits A Public Shadows Function Add(ByVal x As Integer, ByVal y As Integer) As Integer Return x - y End Function End Class 

A volte un piccolo esempio aiuta davvero a capire la differenza in modo tecnico.

 Sub Main() Dim o As New ChildClass Console.WriteLine(o.GetValOverride()) ' Prints 2 Console.WriteLine(o.GetValShadow()) ' Prints 2 Console.WriteLine(CType(o, ParentClass).GetValOverride()) ' Prints 2 Console.WriteLine(CType(o, ParentClass).GetValShadow()) ' Prints 1 Console.ReadLine() End Sub Class ParentClass Public Overridable Function GetValOverride() As String Return "1" End Function Public Function GetValShadow() As String Return "1" End Function End Class Class ChildClass Inherits ParentClass Public Overrides Function GetValOverride() As String Return "2" End Function Public Shadows Function GetValShadow() As String Return "2" End Function End Class 

La parola chiave “ombre” in sostanza dice “Se chiunque accede a questo object sa che è di questo tipo o di uno dei suoi discendenti, usa questo membro, altrimenti usa quello di base”. L’esempio più semplice di questo potrebbe essere una class base ThingFactory, che include un metodo “MakeNew” che restituisce una cosa e una class CarFactory, derivata da ThingFactory, il cui metodo “MakeNew” restituisce sempre una cosa che sarà del tipo derivato Car. Se una routine sa che una ThingFactory in cui si trova, in particolare una CarFactory, userà un CarFactory.MakeNew (se ne esiste uno) ombreggiato, che può specificare il tipo di ritorno come Car. Se una routine non sa che la sua ThingFactory è in realtà una CarFactory, userà un MakeNew non ombreggiato (che dovrebbe chiamare un metodo MakeDerivedThing overridable interno protetto).

Per inciso, un altro buon uso delle ombre è impedire alle classi derivate di accedere a metodi protetti che non funzioneranno più. Non c’è modo di hide semplicemente un membro da classi derivate diverse dall’assegnarne uno nuovo, ma si può impedire alle classi derivate di fare qualsiasi cosa con un membro protetto dichiarando una nuova class vuota protetta con quel nome. Ad esempio, se si chiama MemberwiseClone su un object si rompe, si può dichiarare:

   Protected Shadows Class MemberwiseClone
   End Class

Si noti che questo non viola i principi OOP come il Principio di sostituzione di Liskov, poiché ciò si applica solo nei casi in cui una class derivata potrebbe essere utilizzata al posto di un object di class base. Se Foo e Bar ereditano da Boz, un metodo che accetta un parametro Boz può essere legittimamente passato in Foo o Bar. D’altra parte, un object di tipo Foo saprà che il suo object di class base è di tipo Boz. Non sarà mai nient’altro (ad esempio è garantito che non sia un Bar).

Un esempio di ombreggiatura: supponiamo di voler utilizzare una funzione in un componente di terze parti, ma la funzione è protetta. È ansible ignorare questo vincolo con ereditarietà semplice ed esporre una funzione ombreggiata che fondamentalmente chiama la sua funzione di base:

 Public Class Base Protected Sub Configure() .... End Sub End Class Public Class Inherited Inherits Base Public Shadows Sub Configure() MyBase.Configure() End Sub End Class 

Penso che ci siano davvero due scenari che le persone stanno assumendo qui ed entrambi sono legittimi. Potresti davvero suddividerli nel progettista della class base e negli anni successivi allo sviluppatore che sta implementando la sottoclass che non può modificare la class base. Quindi sì, la cosa migliore da fare è ignorare se hai quel lusso. Questo è l’approccio OOD pulito.

D’altra parte potresti avere qualcosa di simile a un esempio di cui sopra in cui ti trovi all’altro estremo di questa equazione che deve implementare una sottoclass e non puoi cambiare il fatto che il metodo che hai bisogno di sovrascrivere non è contrassegnato come sovrascrivibile. Prendi ad esempio

 Public Shadows Function Focus() As Boolean txtSearch.Focus() Return MyBase.Focus() End Function 

In questo caso sto ereditando la mia class dalla class di controllo Winform che purtroppo non è contrassegnata come sovrascrivibile. A questo punto mi trovo a dover semplicemente rendere il codice “puro” o renderlo più facile da capire. Il client di questo controllo vuole semplicemente chiamare control.Focus () e probabilmente non gli interessa. Avrei potuto chiamare questo metodo FocusSearchText () o Focus2, ecc. Ma credo che quanto sopra sia molto più semplice per il codice client. È vero che se il client esegue questo controllo come class base e chiama Focus, il mio codice non verrà inserito. Ma questo è abbastanza remoto.

Alla fine si arriva a una chiamata di giudizio, e uno che dovrete fare.

Questo è un collegamento MSDN recente: differenze tra shadowing e override

Shadowing protegge da una modifica successiva della class base che introduce un membro che hai già definito nella tua class derivata. Normalmente usi lo shadowing nei seguenti casi:

** Prevedete che la vostra class base possa essere modificata per definire un elemento usando lo stesso nome del vostro. *

** Vuoi la libertà di cambiare il tipo di elemento o la sequenza di chiamata. *

(Devo ancora indagare sull’utilizzo per quanto riguarda l’ambito e i tipi)

Shadow ti permette di fare certe cose che non possono essere fatte con le sostituzioni.

Nel mio caso: ho diverse classi di tabelle con funzionalità generiche; ma per chi le collezioni stesse sono di diverso tipo.

 Public Class GenericTable Protected Friend Overridable Property Contents As System.Collections.Generic.List(Of GenericItem) ... do stuff ... End Class 

Allora ho delle specifiche attenzioni:

 Public Class WidgetTable Inherits GenericTable Protected Friend Shadows Property Contents As System.Collections.Generic.List(Of Widget) ... stuff is inhereted ... End Class 

Non ho potuto ignorare perché il tipo è cambiato.

Ho trovato un’altra differenza. Guarda questo:

 Sub Main() Dim X As New Derived Dim Y As Base = New Derived Console.WriteLine("X:" & X.Test()) Console.WriteLine("Y:" & Y.Test()) Console.WriteLine("X:" & CType(X, Base).Test) Console.WriteLine("X:" & X.Func()) Console.WriteLine("Y:" & Y.Func()) Console.WriteLine("X:" & CType(X, Base).Func) Console.ReadKey() End Sub Public Class Base Public Overridable Function Func() As String Return "Standard" End Function Function Test() As String Return Me.Func() End Function End Class Public Class Derived Inherits Base Public $$$ Function Func() As String Return "Passed By Class1" & " - " & MyBase.Func End Function End Class 

Se si sta usando Overrides (dove c’è $$$) NON C’È MODO di usare Func sulla class Base se la definizione dell’istanza è Derivata e se la definizione è base ma l’istanza è del tipo Derivato.

Se si utilizza Shadows, l’unico modo in cui è ansible visualizzare Func nella class derivata consiste nel definire l’istanza come Derivata e senza passare a un metodo di class base (X.Test restituisce lo standard). Penso che questo sia il principale: se utilizzo Shadows, il metodo non sovraccaricherà il metodo di base all’interno dei metodi di base.

Questo è l’approccio OOP di sovraccarichi. Se ottengo una class e IN NESSUN CASO voglio che venga chiamato un metodo, devo usare Overloads. Per le istanze dei miei oggetti, non c’è modo di restituire “Standard” (ad eccezione dell’utilizzo di riflessioni, penso). Penso che l’intellisense faccia un po ‘di confusione. Se evidenzio Y.Func, verrà evidenziato il Func nella class base, ma viene eseguito il Func nella class derivata.

Con Shadows, il nuovo metodo è raggiungibile solo direttamente. Come ad esempio sovraccarichi, ma nascondendo i sovraccarichi della class base (penso che sia un errore restituito prima della compilazione, perché puoi chiamarlo usando un cast, come implicito fatto usando un sovraccarico).

Bene ecco la risposta dal codice.

 Module Module1 Sub Main() Dim object1 As Parent = New Child() Console.WriteLine("object1, reference type Parent and object type Child") object1.TryMe1() object1.TryMe2() object1.TryMe3() Console.WriteLine("") Console.WriteLine("") Console.WriteLine("object2, reference type Child and object type Child") Dim object2 As Child = New Child() object2.TryMe1() object2.TryMe2() object2.TryMe3() Console.ReadLine() End Sub End Module Public Class Parent Public Sub TryMe1() Console.WriteLine("Testing Shadow: Parent.WriteMe1") End Sub Public Overridable Sub TryMe2() Console.WriteLine("Testing override: Parent.WriteMe2") End Sub Public Sub TryMe3() Console.WriteLine("Testing Shadow without explicitly writing shadow modifier: Parent.WriteMe3") End Sub End Class Public Class Child Inherits Parent Public Shadows Sub TryMe1() Console.WriteLine("Testing Shadow: Child.WriteMe1") End Sub Public Overrides Sub TryMe2() Console.WriteLine("Testing override: Child.WriteMe2") End Sub Public Sub TryMe3() Console.WriteLine("Testing Shadow without explicitly writing shadow modifier: Child.WriteMe3") End Sub End Class 'Output: 'object1, reference type Parent and object type Child 'Testing Shadow: Parent.WriteMe1 'Testing override: Child.WriteMe2 'Testing Shadow without explicitly writing shadow modifier: Parent.WriteMe3 'object2, reference type Child and object type Child 'Testing Shadow: Child.WriteMe1 'Testing override: Child.WriteMe2 'Testing Shadow without explicitly writing shadow modifier: Child.WriteMe3 

Puoi copiare incolla questo e provalo tu stesso. Come puoi vedere, lo shadowing è il comportamento predefinito e Visual Studio ti avvisa quando lo shadowing sta succedendo senza che tu scriva esplicitamente il modificatore shadow.

Nota: per me, non ho mai usato un riferimento alla class Base per un object figlio. Per questi casi uso sempre le interfacce.

Sono d’accordo con Jim. Nemmeno io ho mai trovato un uso legittimo per Shadows. Di solito, se lo vedo, suppongo che la sottosezione del codice debba essere rifattorizzata un po ‘.

Suppongo che è lì in modo che è ansible ombreggiare un metodo da un assembly in cui non si ha il controllo sul codice sorgente. In tal caso, il refactoring della class genitore sarebbe imansible.

Volevo usare System.Web.HttpContext.Current.Response invece di Response.redirect , e avevo bisogno della praticità di codificare come Response.redirect . Ho definito una proprietà readonly chiamata Response per ombreggiare l’originale in una class base. Non è ansible utilizzare le sostituzioni, poiché questa proprietà non è sovrascrivibile. Molto conveniente:)

Le ombre possono essere molto utili se stai scrivendo un wrapper attorno a un controllo esistente.

Ad esempio intorno a una casella combinata. Omigrando l’ AutoCompleteSource è ansible evitare che sia impostato su un valore illegittimo per il tuo tipo speciale di combobox anche quando è trasmesso su una normale casella combinata. Oppure mybase.AutoCompleteSource = value un pre-processo prima di utilizzare mybase.AutoCompleteSource = value nella proprietà shadowing.

L’uso di Shadows è raro ma vero. Inoltre non è ansible sovrascrivere un metodo (statico) condiviso. Quindi devi ombreggiare un metodo condiviso se vuoi “sovrascriverlo”.