We’ll create a sample project to demonstrate why its a bad thing to do.
Start a new desktop project.
Add a new Class that is a subclass of âCancelButtonâ by dragging a CancelButton from the library to the Navigator.
Name this new class âmyPushButtonâ
Add a property to it – enabled as boolean
Make sure the property is public
Now on Window1 add an instance of this new button subclass.
Name it âMyPbâ
Now add a bevel button and name it bevTestSubclass.
Change its Caption to say âbevTestSubclassâ
In its action event put
myPb.enabled = Not myPb.enabled
myPb.invalidate
Add a second bevel button and name it bevTestBaseClass
Change its Caption to say âbevTestBaseClassâ
In its action put
PushButton(myPb).enabled = Not PushButton(myPb).enabled
PushButton(myPb).invalidate
Save (if you care to) and Run
If you push the bevTestBaseClass button the button enables & disables on each push. And it does this visibly.
But if you click the bevTestSubclass button nothing happens.
Why not ?
Properties are not virtual. Quite literally there are two âenabledâ properties and they are not the same one. The custom subclass contains one. And the base class contains one.
So when the code knows you are referring to the subclass type the âEnabledâ property that is used is the one in the subclass instance. And the framework doesnât look at this one at all.
So the framework looks at the one in PushButton and redraws with whatever Enabled state that one says.
And its not the same as the one in the subclass.
But when you press the bevTestBaseClass button you do get the change you expected because the cast makes the code refer to the Superclass version of enabled.
This specific use where a cast is involved isnât so hard to find with a search and replace. But itâs also not the only way that shadowed properties cause problems. The really tricky ones are where the declared type of a variable is the superclass type and then you get much the same behaviour as with the casts. And this one is NOT easy to find.
Weâll demonstrate by adding additional code to the project so far.
First lets define a new method – FlipSubClass – that takes a myPushbutton as a parameter.
Public Sub flipSubclass(button as myPushButton)
button.enabled = Not button.enabled
button.invalidate
End Sub
And for good measure weâll add a new method – FlipSuperClass – that takes a Pushbutton as a parameter
Public Sub flipSuperClass(button as PushButton)
button.enabled = Not button.enabled
button.invalidate
End Sub
The only difference between these two is the type of the parameter.
Now lets add two more bevel buttons to use these new methods.
Add a bevelbutton and name it bevDimSubClass and set the caption to bevDimSubClass.
In this bevel buttons action event put
flipSubClass(myPb)
Add a second bevel button named bevDimSuperClass and set the caption to bevDimSuperClass
And in this action event put
flipSuperClass(myPb)
And run.
Note this time there is NO explicit cast required and we can safely pass a myPushButton to a method that takes a PushButton because a subclass IS always also the super class type (isA would be true if you checked myPb isa PushButton)
And yet the bevDimSubclass button has no effect on the buttons appearance but the bevDimSuperClass button does.
Its the exact same issue as before but this time itâs even harder to detect because what natters here is not the actual type or the instance but the DECLARED type of the parameter.
Since shadowing can cause such hard to track down bugs it better to avoid it.
Now IF you cannot and really to require a subclass have its own âEnabledâ property that actually works you should add it as a COMPUTED PROPERTY that in the getter simply casts SELF to the super type and returns theâs supers property value. And in the getter again cast to the super type and set the supers property to that value given.
And hereâs a sample that shows the bad version and how to make it work as expected