''Sombras'' vs. ''Anulaciones'' en VB.NET
oop override (16)
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
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.
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.
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.
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".
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
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 compilación, porque puedes llamarlo usando un molde, como por ejemplo implícitamente haciendo uso de una sobrecarga).
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 alcance y los tipos)
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.
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:
Protected Shadows Class MemberwiseClone End Class 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).
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.
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:)
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.
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
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.
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 compilador 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.