‘Sombras’ vs. ‘Anulaciones’ en VB.NET

¿Cuál es el significado de las dos palabras clave Shadows and Overrides ? ¿Qué hacen y para qué contexto es preferible uno u otro?

No consideraría que Shadows fuera realmente un concepto de OOP. Las anulaciones indican que está proporcionando funcionalidad nueva o adicional para un método / propiedad, etc. que se declaró en una clase ancestra. Las sombras realmente engañan al comstackdor para que piense que el método / propiedad principal, etc., ni siquiera existe.

No tengo uso para Shadows. Mantener las anulaciones. Este tipo de pequeñas características útiles que VB ha proporcionado durante años siempre terminan causando dolor en algún momento.

Las anulaciones son el calificador más normal. Si la clase secundaria redefine una función de clase base de esta manera, independientemente de cómo se haga referencia a un objeto secundario (utilizando una clase base o una referencia de clase secundaria), se llama a la función secundaria.

Por otro lado, si la función de clase infantil sombrea la función de clase base, entonces un objeto secundario al que se accede a través de una referencia de clase base usará esa función de clase base, a pesar de ser un objeto secundario.
La definición de función hija solo se usa si se accede al objeto secundario utilizando una referencia secundaria coincidente.

El sombreado probablemente no hace lo que crees que hace.

Considere las siguientes clases:

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 

Ahora los uso:

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

Probablemente pienses que O y OB tienen el mismo derecho?

Nop.

oA.fx = 0 mientras oB.fx = 1

Imho, este es un comportamiento muy peligroso y apenas se menciona en los documentos.

Si hubiera utilizado la anulación, serían lo mismo.

Entonces, si bien hay usos legítimos para las sombras, lo más probable es que lo que sea que estés haciendo no sea uno de ellos y debería evitarse.

Anulaciones – Extender o crear una funcionalidad alternativa para un método.

Ejemplo: agregue o amplíe la funcionalidad del evento Paint de una ventana.

 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 

Sombras: redefine un método heredado y fuerza su uso para todas las clases instanciadas con ese tipo. En otras palabras, el método no está sobrecargado pero redefinido y los métodos de la clase base no están disponibles, forzando así el uso de la función declarada en la clase. Las sombras conservan o conservan la definición del método de modo que no se destruya si se modifican los métodos de la clase base.

Ejemplo: Fuerce todas las clases “B” para usar su oddball Agregue la definición de tal manera que si se modifican los métodos A de una clase, no afectará el agregado de B. (Oculta todos los métodos de “Agregar” de la clase base. No podrá llamar a A.Add (x, y, z) desde una instancia de 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 

En algún momento, un pequeño ejemplo realmente ayuda a entender la diferencia de una manera técnica.

 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 palabra clave “sombras” básicamente dice “Si quien está accediendo a este objeto sabe que es de este tipo o uno de sus descendientes, use este miembro; de lo contrario, use el de base”. El ejemplo más simple de esto podría ser una clase base ThingFactory, que incluye un método “MakeNew” que devuelve Thing, y una clase CarFactory, derivada de ThingFactory, cuyo método “MakeNew” siempre devuelve un Thing que será del tipo derivado Car. Si una rutina sabe que una ThingFactory que contiene pasa, más particularmente, a ser una CarFactory, entonces usará una CarFactory.MakeNew sombreada (si existe), que puede especificar el tipo de devolución como Car. Si una rutina no sabe que su ThingFactory es en realidad una CarFactory, utilizará una MakeNew no sombreada (que debe llamar a un método de valorización MakeDerivedThing invalidante interno protegido).

A propósito, otro buen uso de las sombras es evitar que las clases derivadas accedan a los métodos protegidos que ya no funcionarán. No hay forma de simplemente ocultar un miembro de las clases derivadas, además de asignar uno nuevo, pero se puede evitar que las clases derivadas hagan cualquier cosa con un miembro protegido al declarar una nueva clase vacía protegida con ese nombre. Por ejemplo, si llamar a MemberwiseClone en un objeto lo rompería, se puede declarar:

   Clase de sombras protegidas MemberwiseClone
   Clase final

Tenga en cuenta que esto no viola los principios de OOP como el Principio de sustitución de Liskov, ya que solo se aplica en los casos en que una clase derivada se podría usar en lugar de un objeto de clase base. Si Foo y Bar heredan de Boz, un método que acepta un parámetro de Boz puede pasarse legítimamente en un Foo o Bar. Por otro lado, un objeto de tipo Foo sabrá que su objeto de clase base es de tipo Boz. Nunca será otra cosa (por ejemplo, está garantizado que no será un Bar).

Un ejemplo de sombreado: supongamos que desea utilizar una función en un componente de un tercero, pero la función está protegida. Puede omitir esta restricción con herencia simple y exponer una función sombreada que básicamente llama a su función 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 

Creo que en realidad hay dos escenarios que las personas están asumiendo aquí y ambos son legítimos. Realmente podría dividirlos en el diseñador de la clase base y el desarrollador años después, quién está implementando la subclase que no puede modificar la clase base. Entonces, sí, lo mejor que puede hacer es anular si tiene ese lujo. Este es el enfoque limpio de OOD.

Por otro lado, puede tener algo así como el ejemplo anterior donde se encuentra en el otro extremo de esta ecuación al tener que implementar una subclase y no puede cambiar el hecho de que el método que necesita anular no está marcado como sobrescribible. Toma por ejemplo

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

En este caso, estoy heredando mi clase de la clase de control Winform, que desafortunadamente no está marcada como invalidable. En este punto, me enfrento solo con hacer que el código sea “puro” o que sea más fácil de entender. El cliente de este control simplemente quiere llamar control. Focus () y probablemente no le importe. Podría haber llamado a este método FocusSearchText () o Focus2, etc. pero creo que lo anterior es mucho más simple para el código del cliente. Es cierto que si el cliente arroja este control como clase base y llama a Focus, mi código no se ejecutará. Pero eso es bastante remoto.

Al final todo se reduce a un juicio crítico, y uno tendrá que hacer.

Este es un enlace reciente de MSDN: diferencias entre sombreado y anulación

El sombreado protege contra una modificación subsiguiente de la clase base que introduce un miembro que ya ha definido en su clase derivada. Normalmente usa el sombreado en los siguientes casos:

** Anticipa que su clase base podría modificarse para definir un elemento con el mismo nombre que el suyo. *

** Desea la libertad de cambiar el tipo de elemento o la secuencia de llamada. *

(Todavía tengo que investigar el uso con respecto al scope y los tipos)

Shadow te permite hacer ciertas cosas que no se pueden hacer con anulaciones.

En mi propio caso: tengo varias clases de tablas con funcionalidad genérica; pero para quienes las colecciones son de diferentes tipos.

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

Entonces tengo isntances específicos:

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

No pude anular porque se cambió el tipo.

Encontré otra diferencia. Mira esto:

 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 

Si está utilizando Anulaciones (donde hay $$$) NO HAY FORMA DE usar Func en la clase Base, ya sea si la definición de la instancia es Derivada, y si la definición es la base pero la instancia es del tipo Derivado.

Si está utilizando Sombras, la única forma en que puede ver el Func en la clase derivada es definir la instancia como Derivada, y sin pasar a un método de clase base (X.Test devuelve Estándar). Creo que este es el principal: si uso Shadows, el método no sobrecargará el método base dentro de los métodos base.

Este es el enfoque OOP de Sobrecargas. Si derivo una clase y EN NINGÚN CASO quiero que se llame a un método, tengo que usar Sobrecargas. Para las instancias de mis objetos, no hay forma de devolver “Estándar” (excepto el uso de reflexiones, creo). Creo que intellisense crea un poco de confusión. Si destaco Y.Func, se resaltará el Func en la clase base, pero se ejecutará el Func en la clase derivada.

Con Shadows, el nuevo método es accesible solo directamente. Como Overloads, pero ocultando las sobrecargas de la clase base (creo que es un error que se devuelve antes de la comstackción, porque puedes llamarlo usando un molde, como por ejemplo implícitamente haciendo uso de una sobrecarga).

Bueno, aquí está la respuesta por Código.

 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 

Puedes copiar pegar esto y probarlo tú mismo. Como puede ver, el sombreado es el comportamiento predeterminado, y Visual Studio le advierte cuando se está produciendo un sombreado sin que usted escriba explícitamente el modificador oculto.

Nota: Para mí, nunca he usado una referencia de clase base para un objeto secundario. Para tales casos siempre uso Interfaces.

Estoy de acuerdo con Jim. Nunca he encontrado un uso legítimo para Shadows, tampoco. Por lo general, si lo veo, supongo que la subsección del código debe ser refactorizada un poco.

Supongo que está ahí para que pueda ocultar un método de un ensamblado en el que no tiene control sobre el código fuente. En ese caso, la refacturación de la clase principal sería imposible.

Quería usar System.Web.HttpContext.Current.Response lugar de Response.redirect , y necesitaba la conveniencia de codificar como Response.redirect . Definí una propiedad de solo lectura llamada Response para sombrear el original en una clase base. No pude usar anulaciones, ya que esta propiedad no es reemplazable. Muy conveniente:)

Las sombras pueden ser muy útiles si está escribiendo un contenedor alrededor de un control existente.

Por ejemplo, alrededor de un cuadro combinado. Al sombrear AutoCompleteSource puede evitar que se establezca en un valor ilegítimo para su tipo especial de combobox incluso cuando se lo transfiere a un combobox normal. O haga un poco de preprocesamiento antes de usar mybase.AutoCompleteSource = value en la propiedad de sombreado.

El uso de Shadows es raro pero cierto. Además, no puede anular un método compartido (estático). Por lo tanto, debe sombrear un método compartido si desea “anularlo”.