I have this question about c# language’s dynamic binding behavior.
Consider the following object hierarchy:
object
System.Windows.Forms.Control
ClassA
ClassB
MyClass
There might be additional classes in between but these are the ones we should consider. Object and Control classes are .NET Framework classes. ClassA and ClassB are third party library classes and MyClass is … well, my class.
I’ve overridden “Control”s TabStop property in MyClass. The property might be overridden somewhere else in the hierarchy, but I don’t think that really matters.
(MyClass is in another assembly which is a vb.net project)
Public Overrides Property TabStop As Boolean
Get
Return MyBase.TabStop
End Get
Set(value As Boolean)
MyBase.TabStop = value
End Set
End Property
And last, please consider the following code. Note that some of the objects in myControlCollection is of type MyClass, others are not.:
foreach (Control c in myControlCollection)
{
if (c is ClassA)
{
if (((ClassA)c).Properties.ReadOnly) c.TabStop = false;
}
}
Now comes the question:
I’ve set a breakpoint in MyClass’s TabStop property’s setter method.
The breakpoint is not hit for ANY object in the collection if the code runs as given above.
If I change the line as…
((ClassA)c).TabStop = false;
… visual studio hits the breakpoint in MyClass declaration.
This confuses me. Why is not the breakpoint hit when the property is called through a variable of type “Control”. Even though the variable is of type Control, the actual object is of type MyClass, so I believe the breakpoint should be hit.
And second, if it does not hit when called through a variable of type Control, why does it hit when I cast the variable to ClassA. I am not casting it to MyClass, I am casting it to a base class, which probably has its own implementation of TabStop, or maybe inherits from Control. Either case, it is not my code.
Can anybody please explain this behavior?
You have not actually overriden the
TabStopproperty, because it is not virtual.What you have done is “hidden” it by creating another property with the same name. Therefore, a different property setter is executed when you try to set
Control.TabStophere:And when you set it here:
When you refer to the property, the compiler resolves the name using static binding because it is not virtual. For that reason if you do not cast the object to something more derived than
Control, you will not see your own code run.Update: This still leaves some open questions:
MyControl.TabStopif the static type of the variable isClassA? Shouldn’t it still bind toControl.TabStop?Public Overrides Property TabStop As BooleanifControl.TabStopis not virtual?We know that there has to be some class in the hierarchy between
ControlandMyControlwith a virtualTabStopproperty (otherwiseOverridesonMyControl.TabStopwould be a compiler error). We also know thatClassA.TabStopends up binding toMyControl.TabStop. Assuming that there is no other class in the hierarchy betweenControlandClassA, there is only one logical explanation: classClassAdefines a virtualTabStopproperty thatShadowsControl.TabStop.