|< Day Day Up >|
In general, derived classes should not declare members with the same name as a base class member. When the same programmer defines both the base class and the derived classes, this rule is fairly easily followed. But when two programmers (or even two companies) define the base class and derived classes separately, avoiding name collisions is difficult or impossible .
For example, imagine a class library sold by SmithCorp that contains the following class.
Class Base ... End Class
A developer from DoeCorp purchases the class library and uses it to build an application. In that application, the developer derives a class from the Base class and defines a Print method.
Class Derived Inherits Base Sub Print() ... End Sub End Class
Everyone's happy. A year later, SmithCorp releases version 2.0 of its class library. This new version contains many bug fixes and new features, and DoeCorp wants to use the new version as the basis for version 2.0 of their application. So they buy the new version and rebuild their application. However, unbeknownst to DoeCorp, the developers at SmithCorp have added a new method, Print , to the class Base .
Class Base ... Sub Print() ... End Sub End Class
This new method has the same name and signature as the method that DoeCorp defined in their derived class. What happens now?
One solution in this case would be to simply rename Derived.Print to something else. However, this may not be practical ”it's possible that DoeCorp is writing a class library that other customers already are using, or the cost of replacing all calls to Derived.Print may be too great ( especially if this problem occurs with each new version of Base ). Instead, when a member in a derived class has the same name as a member in a base class, the derived member hides the base member. This is also called shadowing one member with another. Shadowing is automatically done by the compiler, but it will give a warning unless the code explicitly acknowledges that the shadowing is occurring.
Class Derived Inherits Base Shadows Sub Print() ... End Sub End Class
The Shadows keyword applied to the declaration of Derived.Print states that the method shadows any base members by the same name. The one situation in which the Shadows keyword can be omitted without getting a warning is when a derived class member shadows an inaccessible base class member.
Shadows is allowed on a declaration even if the declaration doesn't shadow anything in a base class. Adding Shadows to a declaration that doesn't shadow anything allows "preemptive" name hiding, to protect against likely future conflicts. It's also important to note that Shadows can be used on any kind of declaration and that Shadows can be used to hide any kind of declaration. In the following example, the Print field shadows the base Print method.
Class Derived Inherits Base Shadows Print As Boolean End Class
If one overloaded method is declared as Shadows , they all have to be, since they all hide the same name. For example, if Print were overloaded in Derived , it would have to be declared as follows .
Class Derived Inherits Base Shadows Sub Print() ... End Sub Shadows Sub Print(ByVal o As Object) ... End Sub End Class
Shadowing and Overriding
It is easy to get confused over the differences between shadowing and overriding, so it is worth discussing this one point in detail. When a method in a derived class overrides a base class method, it replaces the base class's implementation of the method. When the method is called, the base class method is still the one being called. In the following code, the Main method is still calling the Base.Print method ”it is just that the actual implementation of the method called at runtime is the one supplied by Derived.Print .
Class Base Overridable Sub Print() ... End Sub End Class Class Derived Inherits Base Overrides Sub Print() ... End Sub End Class Module Test Sub Main() Dim d As Derived = New d() Dim b As Base = d ' This calls the implementation supplied by Derived.Print b.Print() ' This also calls the implementation supplied by Derived.Print d.Print() End Sub End Module
Whether the instance is typed as Base or Derived makes no difference in this case, because what Derived did was replace Base 's implementation of the method Print .
In contrast, when a method in a derived class shadows a base class method, it only hides the base class method. Because of this, the stated type of an instance is significant. If an instance is typed as the derived class, the base class method is hidden. If an instance is typed as the base class, the base class method suddenly becomes visible again because the derived class no longer hides the method. In the following code, the Main method calls the Base.Print method in the first call and the Derived.Print method in the second.
Class Base Sub Print() ... End Sub End Class Class Derived Inherits Base Shadows Sub Print() ... End Sub End Class Module Test Sub Main() Dim d As Derived = New Derived() Dim b As Base = d ' This calls Base.Print b.Print() ' This calls Derived.Print d.Print() End Sub End Module
In this case, whether the instance is typed as Base or Derived matters very much as to which Print method is visible at any one time.
This is an extremely subtle point but one that's important to understand. Shadowing is not a way to "override" a method that was not declared overridable.
Shadowing and Accessibility
Another important point to keep in mind is that when a derived member shadows a base member, it only hides the base member in contexts where the derived member is accessible. For example, in the following situation, the Main method calls Base.Print because Derived.Print is declared as Private . Since Derived.Print is not accessible to Main , it does not hide Base.Print from Main 's perspective.
Class Base Public Sub Print() ... End Sub End Class Class Derived Inherits Base Private Shadows Sub Print() ... End Sub End Class Module Test Sub Main() Dim d As Derived = New Derived() ' This calls Base.Print because Derived.Print is not accessible d.Print() End Sub End Module
This rule may seem obscure, but it allows new members to be added to a base class without an inaccessible derived class member "blocking" the new member. Without this rule, there would be no way for the preceding Main class to access Base.Print through the type Derived , something that would be unfortunate given that Derived.Print is Private and can never be accessed outside of Derived .
Default properties have an interesting interaction with shadowing. Default properties can be accessed in two ways: as the default property of a type and as a regular property. As a result, a member can shadow the default property's property name but still allow the property to be accessed as the default property of a type.
Class Base Default ReadOnly Property Item(ByVal Index As Integer) As Integer Get ... End Get End Property End Class Class Derived Inherits Base ' This only shadows the Item name because it's not declared as Default Shadows ReadOnly Property Item(ByVal Index As Integer) As Integer Get ... End Get End Property End Class Module Test Sub Main() Dim x As Derived = New Derived() ' This calls Base.Item Console.WriteLine(x(10)) ' This calls Derived.Item Console.WriteLine(x.Item(10)) End Sub End Module
Similarly, a default property in a derived class can shadow a base class default property but not shadow the base default property's name.
Class Base Default ReadOnly Property Item(ByVal Index As Integer) As Integer Get ... End Get End Property End Class Class Derived Inherits Base ' This only shadows the default property because it has a different ' name Shadows Default ReadOnly Property Value(ByVal Index As Integer) _ As Integer Get ... End Get End Property End Class Module Test Sub Main() Dim x As Derived = New Derived() ' This calls Derived.Value Console.WriteLine(x(10)) ' This calls Base.Item Console.WriteLine(x.Item(10)) End Sub End Module
|< Day Day Up >|