About Visual Basic features several articles about using procedure modifiers such as Shadows, Overrides, and Overloads. For example, Procedures in VB.NET is an overview of the whole subject and a three article mini series covers the modifiers above individually. The article that covers Shadows can be found at Shadows in VB.NET.
The common theme in those articles is to explain how these keywords can add flexibility and power to your code. But there's a dark side too. Shadows is inherently powerful enough to result in what can be called, "unintended consequences". This article is all about what can go wrong when you use shadows by illustrating just how powerful it can be.
To get started, here's the example code we will use. It consists of a base class and a derived class. Assume that the base class can't be changed for some reason. Maybe it's part of a vendor code library, for example. The derived class is the one in our application. In the code below, it does absolutely nothing. It will be changed to illustrate what can happen when using Shadows.
Important Note: If your class can't be inherited, this is simply not a problem anymore. That's a big reason why classes in mny libraries can't be inherited.
Public Class BaseClass
Public ReadOnly Property BaseProperty As Long
Dim aYear As Integer = Now.Year
Dim aMonth As Integer = Now.Month
Dim aDay As Integer = Now.Day
Get
Dim theDate As Long
theDate =
DateSerial(
aYear, aMonth, aDay
).Ticks
Return theDate
End Get
End Property
End Class
Public Class DerivedClass
Inherits BaseClass
' This class does NOTHING right now
End Class
If you instantiate DerivedClass in your application, you get an interesting binary equivalent of the current date and time:
Dim theClass1 As New DerivedClass
Console.WriteLine(theClass1.BaseProperty.ToString)
This is fairly useless since the purpose is simply to illustrate Shadows.
*** Side Note ***
Because I write a lot of technical articles, I'm very interested in whether it's more useful to illustrate articles with real-world examples ...
account number, inventory amount, customer name
... or generic code as I have here ...
BaseClass, DerivedClass, theDate
If you have an opinion, let me know.
*** End of Side Note ***
BaseClass returns a long integer containing a binary equivalent of a date using the Ticks property. But Ticks isn't really intended to do that. (An analysis of Ticks can be found here: Of Ticks and Timers.) Suppose that you decide that your code should use something that is designed to be a binary equivalent: ToBinary. The problem is that you can't change BaseClass and you can't use Overrides because the base class has to specifically allow Overrides with the Overrideable keyword. The answer is to shadow the class. Here's the new class:
Public Class DerivedClass
Inherits BaseClass
Dim aYear As Integer = Now.Year
Dim aMonth As Integer = Now.Month
Dim aDay As Integer = Now.Day
Public Shadows ReadOnly Property BaseProperty As Long
Get
Dim theDate As Long
theDate =
DateSerial(
aYear, aMonth, aDay
).ToBinary
Return theDate
End Get
End Property
End Class
The value returned is the result of a function (ToBinary), not a property (Ticks). In this case, you get the same result that you would get if you could use Overrides.
Since Shadows is the default, it's technically not required. The compiler issues a warning, but the code works anyway. It's a good idea to use Shadows to make your code clear and get rid of the warning.
You can still instantiate the base class directly.
Dim theClass1 As New BaseClass
If you do, then you get the old Ticks property. Or, you can instantiate the base class as a New derived class.
Dim theClass1 As BaseClass = New DerivedClass
If you do this, you still get the old Ticks property. I wrote an entire article explaining why: Declaring Reference Types in VB.NET. This is different behavior than you would get with Overrides. Overrides replaces the reference to the base class property entirely, so you always get the new ToBinary function. It can be confusing.
In fact, you can totally replace the whole thing with something quite different. The existing code is readonly. Suppose you wanted to replace it with a read/write version. In this case, you will also have to figure out how you're going to save the value because you'll have to write a Set clause for the BaseProperty property to complement the existing Get clause. Just to make it interesting, I've demonstrated how to save the property internally as a Date data type but continue to pass and receive it with calling code as a binary value using both ToBinary and FromBinary
Public Class DerivedClass
Inherits BaseClass
Private BasePropertyValue As Date
Public Shadows Property BaseProperty As Long
Get
Return DateSerial(
BasePropertyValue.Year,
BasePropertyValue.Month,
BasePropertyValue.Day
).ToBinary
End Get
Set(value As Long)
BasePropertyValue =
DateTime.FromBinary(value)
End Set
End Property
End Class
There is now virtually nothing the same about DerivedClass. You can add the new read/write function to the calling code:
theClass1.BaseProperty = 12345678901234
Console.WriteLine(theClass1.BaseProperty.ToString)
theClass1.BaseProperty = 98765432109876
Console.WriteLine(theClass1.BaseProperty.ToString)
You can even change it into a method instead of being a property.
Public Shadows Sub BaseProperty()
Console.WriteLine(
"DerivedClass BaseProperty as a Method!")
End Sub
' calling code must be changed too!
Dim theClass1 As New DerivedClass
theClass1.BaseProperty()
We started this tip by stating that this can be a risky way to code. The writer of a base class will naturally assume that when you use it, you plan to implement it as it was written, not as something entirely different. Following the theme of "inheritance", it's like you planted sunflowers but dogs and cats started crawling out of the ground instead. Any documentation of the original class is useless. Nothing can be relied on anymore.
Just because you can do it, it may not be a good idea. This is a big reason why the classes in .NET are normally marked NotInheritable. Browse through the Microsoft.VisualBasic library and you'll see that every class there is marked that way.
