In the past I used to shadow properties extensively. Thanks to comments of Kem and Norman in a post started by Tim Jones called Shadowing Properties … Good or Bad? a few days ago I finally “got it” - at least I think I do so…
This is how I would summarize it (virtual methods vs. non-virtual properties):
- When you call a method, the runtime type of the object is used to find out which implementation of the method to use.
- When you call a property, the compile-time type of the object is used to find out which implementation of the property to use.
Translated into code it looks like that:
[code]// Class2 is a subclass of Class1
Dim obj11 As Class1 = New Class1()
Dim obj22 As Class2 = New Class2()
Dim obj12 As Class1 = New Class2()
obj11.Method() // Variable: Class1, Instance: Class1 -> Class1.Method
obj11.Prop = 0 // Variable: Class1, Instance: Class1 -> Class1.Prop
obj22. Method() // Variable: Class2, Instance: Class2 -> Class2.Method
obj22.Prop = 0 // Variable: Class2, Instance: Class2 -> Class2.Prop
obj12. Method() // Variable: Class1, Instance: Class2 -> Class2.Method -> virtual, Method of Class2 is invoked
obj12.Prop = 0 // Variable: Class1, Instance: Class2 -> Class1.Prop -> non-virtual, Prop of Class1 is invoked[/code]
I never had any problems with my shadowed properties, because I never used these classes in a way that the subtle bugs Kem and Norman were talking about could arise. But here is an example I can think of - now that I “got it”:
[code]Class MyCanvas
Property Enabled As Boolean // Shadows Enabled of the built-in super class Canvas
…
Sub Close() // Overrides Close of the built-in super class Canvas
…
End Class
// Now somewhere in a method of the window containing an instances of Canvas and MyCanvas
For i As Integer = 0 To ControlCount - 1
If Control(i) IsA Canvas Then
Dim cnv As Canvas = Canvas(Control(i))
cnv.Enabled = True // Enabled of Canvas will be invoked even if cnv is of type MyCanvas -> DANGEROUS
cnv.Close() // This will work and call Close of Canvas or MyCanvas depending on the type of cnv
End
Next[/code]
I started to replace all shadowed properties in my two large projects with a method pair for setting (with the help of the keyword “Assigns”) and getting the specific value, but I’m stuck when it comes to classes where I don’t have access to the source code: Windows, ContainerControls, and all built-in controls based on RectControl, but also classes from bought plugins. I didn’t find a way to satisfyingly solve this.
One way would be to introduce methods with a different name as the property’s name. For example like that:
[code]// CanvasExtensionsModule
Sub Enabled_OnlyUseThatOne(Extends cnv As Canvas, value As Boolean)
cnv.Enabled = value
End
Function Enabled_OnlyUseThatOne(Extends cnv As Canvas) As Boolean
Return cnv.Enabled
End
// MyCanvas
Sub Enabled_OnlyUseThatOne(value As Boolean)
Super.Enabled = value
// do my other stuff I need to do when the value of Enabled changes
End
Function Enabled_OnlyUseThatOne() As Boolean
Return Super.Enabled
End
[/code]
But now there will be two Enabled methods side by side for both Canvas and MyCanvas - and I can’t prevent that the wrong one will be chosen (Enabled in that case).
So how could a proper solution look like when:
- the source code of a class A is not available
- there is a need to add some code to the setter or getter of a property in a subclass of class A