'Shadows' vs. 'Overrides' in VB.NET

人盡茶涼 提交于 2019-11-27 06:35:35

I wouldn't consider Shadows to really be an OOP concept. Overrides indicates that you are providing new or additional functionality for a method/property etc that was declared in an ancestor class. Shadows really tricks the compiler into thinking that the parent method/property etc does not even exist.

I have no use for Shadows. Stick to Overrides. These types of helpful little "features" that VB has provided for years always end up causing you grief at some point.

Overrides is the more normal qualifier. If the child class redefines a base class function in this way, then regardless of how a child object is referenced (using either a base class or a child class reference) it is the child function that is called.

On the other hand, if the child class function Shadows the base class function, then a child object accessed via a base class reference will use that base class function, despite being a child object.
The child function definition is only used if the child object is accessed using a matching child reference.

Shadowing probably doesn't do what you think it does.

Consider the following classes:

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

Now I use them:

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

You probably think oA and oB are the same right?

Nope.

oA.fx = 0 while oB.fx = 1

Imho this is very dangerous behavior and it's barely mentioned in the docs.

If you had used override they would be the same.

So while there are legitimate uses for shadows, chances are whatever you're doing is not one of them and it should be avoided.

Overrides - Extending or creating alternate functionality for a method.

Example: Add or extended the functionality of the Paint event of a window.


    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

Shadows - Redefines an inherited method and forces its use for all classes instanced with that type. In other words the method is not overloaded but redefined and the base class methods are not available, thus forcing the use of the function declared in the class. Shadows preserves or retains the definition of the method such that it is not destroyed if the base class methods are modified.

Example: Force all "B" classes to use it's oddball Add definition such that if A class Add methods are modified it won't affect B's add. (Hides all base class "Add" methods. Won't be able to call A.Add(x, y, z) from an instance of 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

Sometime a small example really helps understand the difference in a technical way.

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

The "shadows" keyword essentially says "If whoever is accessing this object knows it to be of this type or one of its descendents, use this member; otherwise use the base one." The simplest example of this might be a base class ThingFactory, which includes a "MakeNew" method which returns a Thing, and a class CarFactory, derived from ThingFactory, whose "MakeNew" method always returns a Thing that will be of derived type Car. If a routine knows that a ThingFactory it holds happens to, more particularly, be a CarFactory, then it will use a shadowed CarFactory.MakeNew (if one exists), which can specify the return type as Car. If a routine doesn't know that its ThingFactory is actually a CarFactory, it will use a non-shadowed MakeNew (which should call an internal protected overridable MakeDerivedThing method).

Incidentally, another good use of shadows is to prevent derived classes from accessing Protected methods which will no longer work. There's no way of simply hiding a member from derived classes other than assigning a new one, but one can prevent derived classes from doing anything with a protected member by declaring a new protected empty class with that name. For example, if calling MemberwiseClone on an object would break it, one can declare:

  Protected Shadows Class MemberwiseClone
  End Class
Note that this does not violate OOP principles like the Liskov Substitution Principle, since that only applies in cases where a derived class might be used in place of a base-class object. If Foo and Bar inherits from Boz, a method which accepts a Boz parameter may legitimately be passed in a Foo or Bar instead. On the other hand, an object of type Foo will know that its base-class object is of type Boz. It will never be anything else (e.g. it's guaranteed not to be a Bar).

An example of shadowing: Let's assume that you want to use a function in a third-party component, but the function is protected. You can bypass this constraint with simple inheritance and exposing a shadowed function which basically calls its base function:

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

I think there are really two scenarios that people are taking on here and both are legitimate. You could really break them down into the base class designer and the developer years later who is implementing the subclass who cannot modify the base class. So yes, the best thing to do is override if you have that luxury. This is the clean OOD approach.

On the other hand you may have something like example given above where you are on the other end of this equation having to implement a sub class and you cannot change the fact that the method you need to override is not marked overridable. Take for example

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

In this case I'm inheriting my class from the Winform control class which unfortunately isn't marked as overridable. At this point I'm faced with just making the code "pure" or making it easier to understand. The client of this control simply wants to call control.Focus() and probably doesn't care. I could have named this method FocusSearchText() or Focus2, etc but I believe the above is much more simple for the client code. It's true that if the client then casts this control as the base class and calls Focus my code won't excute. But that is fairly remote.

In the end it comes down to a judgement call, and one you'll have to make.

This is a recent MSDN link: Differences between shadowing and overriding

Shadowing protects against a subsequent base-class modification that introduces a member you have already defined in your derived class. You normally use shadowing in the following cases:

** You anticipate that your base class might be modified to define an element using the same name as yours.*

** You want the freedom of changing the element type or calling sequence.*

(I'm yet to investigate usage with respect to scope and types)

Shadow allows you to do certain things that cannot be done with overrides.

In my own case: I have several tables classes with generic functionality; but for whom the collections themselves are of different types.

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

Then I have specific isntances:

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

I could not override because the the type is changed.

I found another difference. See this:

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

If you are using Overrides (where there is $$$) THERE IS NO WAY to use Func on Base class either if the definition of the instance is Derived, and if the definition is base but the instance is of the Derived type.

If you are using Shadows, the only way you can see the Func into the derived class, is to define the instance as Derived, and without passing to a method of base class (X.Test returns Standard). I think this is the main one: If I use Shadows, the method won't overload the base method inside base methods.

This is the OOP approach of Overloads. If I derive a class and IN NO CASE I want that a method would be called, I have to use Overloads. For instances of my objects, there is no way to return "Standard" (Except using reflections, I think). I think intellisense make a bit of confusion. If I highlight Y.Func, there will be highlighted the Func into base class, but is executed the Func into derived class.

With Shadows, the new method is reachable only directly. As such as Overloads, but hiding the overloads of the base class (I think it's an error returned before the compilation, because you can call it using a cast, as such as implicit done using a overload).

Well here is the answer by Code.

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

You can copy paste this and try it yourself. As you can see the shadowing is the default behavior, and Visual Studio does warn you when shadowing is going on without you explicitly writing the shadow modifier.

Note: For me, I have never used a Base class reference to a child object. For such cases I always use Interfaces.

I agree with Jim. I've never found a legitimate use for Shadows, either. Usually if I see it, I assume that sub-section of the code needs to be refactored a bit.

I suppose it is there so that you can shadow a method from an assembly in which you do not have control over the source code. In that case, refactoring the parent class would be impossible.

I wanted to use System.Web.HttpContext.Current.Response instead of Response.redirect, and needed the convenience to code as Response.redirect. I defined a readonly property named Response to shadow the original in a base class. I couldn't use overrides, since this property is not overridable. Very convenient:)

Brnd

Shadows can be very useful if you are writing a wrapper around an existing control.

For instance around a combobox. By shadowing the AutoCompleteSource you can prevent it to be set to a illegitimate value for your special kind of combobox even when it's cast to a normal combobox. Or do some pre-proccessing before you use mybase.AutoCompleteSource = value in the shadowing property.

The use of Shadows is rare but true. Moreover you cannot override a shared (static) method. So you must shadow a shared method if you want to "override" it.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!