Adding "Caption" property to Label subclass does bad things

I have a label subclass, cLabel, that I’ve been using for a few years. It has a .Caption computed string property, which is also set to be visible and settable in the IDE Behavior.

It appears that starting in 2013 R3, this is now problematic. My guess here is that since the Label class already has a .Caption property, the IDE is getting confused upon loading/saving the controls. If you look in the LR, it says that the “Text” property is the “Same as the Caption property”. So it’s even more confusing…

Looks like I’ve accidentally set up a situation in which we have:
Label.Text
Label.Caption
cLabel.Caption (my subclass’s computed property)
Instance of cLabel.caption which has data set in the IDE’s property Inspector.

It’s amazing this worked as long as it did.

I’m guessing the solution is to rename the .caption property of my subclass to something else - I’m worried that when I do this, I’ll end up losing all the several hundred string constants that I’ve entered in the IDE for my various label subclasses. and am not sure what to do about that.

Hmm…

Property shadowing has always been something we recommend against and that’s what you’ve got set up
I’m amazed it ever worked

Agree - in fact, I just took a look at my “Label” subclass (which used to be a StaticText subclass) and it still has leftovers from the StaticText:

  <ViewProperty>
   <ObjName>Underline</ObjName>
   <Visible>1</Visible>
   <PropertyGroup>Font</PropertyGroup>
   <ItemType>Boolean</ItemType>
   <InheritsFrom>StaticText</InheritsFrom>
  </ViewProperty>
...
 <ViewProperty>
   <ObjName>InitialParent</ObjName>
   <InheritsFrom>StaticText</InheritsFrom>
  </ViewProperty>
  <ViewProperty>
   <ObjName>Caption</ObjName>
   <PropertyGroup>Behavior</PropertyGroup>
   <ItemType>string</ItemType>
   <EditorType>MultiLineEditor</EditorType>
  </ViewProperty>
etc...

So I’m thinking that I probably need to re-create this class from scratch and perhaps then the poor IDE won’t be losing its mind.

I’ve submitted this as a bug, as I can reproduce it w/o any use of StaticTexts. Something clearly changed between 2013 R2 and R3 <https://xojo.com/issue/29630>

Caption was a property long ago (deprecated in favor of using Text) - but it still exists and is a synonym
You’ve shadowed it in your subclass and it’s no surprise it causes problems.

However I do see an issue here that’s not quite that you might think
Try this
change mCaption to a public non-computed property
Analyze the project
You do get a shadowing warning (as its not strictly prohibited)

Don’t save
Reopen your sample
Make a computed string property “Caption as string”
Analyze

I’d say the compiler SHOULD give you a warning here as well but it doesn’t

So this report should be “no warning on computed property that shadows on built in”

But your current situation is not a “bug” in the way you were thinking

I don’t know - if, say you can no longer shadow ANY properties of controls, that would seem to be a non-trivial limitation. For example, if adding a .Left() property to a button class would cause the button instances to lose their .left value when being saved/loaded, that really seems like a bug to me.

Being able to create control subclasses is a very powerful technique - I’d be sad if that were no longer allowed in 2013 R3, unless there is a good reason for the change.

The mystery to me : what changed in R3 that’s causing this? Is it important?

We’ve ALWAYS said “Don’t shadow”.

Subclass all you want but don’t shadow as you’re likely to have issues.

Just out of curiosity and for me to learn does ‘shadow’ mean ‘override’. Does this mean you can override methods but not properties? Some languages have restrictions like if a superclass has a read only property you can’t override in a subclass and provide a read-write.

You can’t overwrite properties, I think.

Does “shadow” mean overload?

Sorry, didn’t see that Carl had asked the same question…

To give some background here: the reason I’m shadowing (overriding) the .Caption property is that I’ve written a thread-safe Label subclass that is a drop-in (*) replacement for the Label class. This is cool, as you can write code like this:

Thread1.Run
  for i = 1 to N
   ...do stuff...
   Label1.Caption = "Processing item " +str(i) + " of " + str(n)
  next

This works great, and is an immense time-saver if you are retrofitting old Carbon or Win32 code that was written under the old threading regime.

(*) The problem is that if you refer to the cLabel class by the Label superclass, then the properties are no longer overridden, e.g.

  dim L as cLabel = Label1
  L.caption = "foobar"  // calls the cLabel method
  dim L2 as Label = Label1
  L2.caption = "foobar" // calls the Label superclass' method which is not thread-safe

So it’s not foolproof, but it sure is nice when it works.

Somehow - I would expect that to be the case :slight_smile:

Well, not all languages do the same thing here. In fact, one can argue that Xojo is somewhat inconsistent : overridden methods are virtualized, but properties are not. See http://en.wikipedia.org/wiki/Virtual_function for background.

Properties have never been virtual - not since the beginning of time as far as Xojo/Real Studio/Real basic are concerned (15+ years now)
You can argue that design decision as much as you’d like but I doubt it will change as the effects are hard to predict.

However, unlike many other languages you can swap out a property for a computed property or get / set pair of methods with NO impact to users of the class. So if you want “virtual properties” in your classes you can use a get / set pair of methods - which are virtual.

THAT is good information. Thanks, Norman. I’m going to have to play with this a little.

I can’t believe you’ve never done this :stuck_out_tongue:
You’ve been around a long time I would have guessed you’d have done this at least once

I try not to shadow properties. But there are one or two instances where it would be handy.

Shadowing is, in almost every instance, a recipe for Bad Things™ to happen

Michaels short bit of code enumerates one of the more obvious issues

dim L as cLabel new Label1
L.caption = “foobar” // calls the cLabel method
dim L2 as Label new cLabel1
L2.caption = “foobar” // calls the Label superclass’ method which is not thread-safe

Try this one for “not obvious behavior”
Class1 is a subclass of label with a computed property “Caption as string”

dim c1 as Label = new Label
c1.Caption = “test”

dim c2 as Label = new Class1
c2.Caption = “test”

Note that c2.Caption will NOT call the computed property setter.
The DECLARED type is whats important - not the type at runtime.
This is usually where shadowing causes issues as its not obvious why your shadowed value is not getting manipulated when you look at the code.

Ah. Then I misunderstood your post. I thought you meant that a computed property would successfully shadow a class property. If it doesn’t, then never mind. Using a computed property with a different name is what I have done and will continue to do.