Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations SkipVought on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

NoDefault Tracing and Unexpected Behavior 1

Status
Not open for further replies.

stanlyn

Programmer
Sep 3, 2003
945
0
16
US
Hi,

Some more nodefault and dodefault questions that has not been answered well enough for my understanding in the several other threads found here.

1. How can I get the debugger to traverse the code contained within my subclassed object as well as the base class so the actual firing order can be examined?

2. There are certain values that are set in the parent class that are being set even though there is a nodefault on the 1st line of the same method (not parent). For example... (note the parent name I use below is invalid in code and only used to convey hierarchy)

Code:
SubclassedForm.Parent.load
public gcVar1
gcVar1 = 'Test'
Contents of the load event in the subclassed form's parent load event. This code can be seen by clicking the "View Parent Code" button.

Code:
SubclassedForm.load
NoDefault
? gcVar1(indent)    && shows 'Test'
This code is shown in the same window that also hosts the 'View Parent Code' button.

I was thinking (incorrectly) that nodefault causes the code in the parent class method to be skipped (as if was never there) and I am not seeing this.

I need to skip the parent code, so how do I do this? I really don't want to mess with the subclassed objects as that would undermine what subclassing is all about, in my opinion. Instead, I would like to do a nodefault (or something) to skip the default method and write my own.

Thanks,
Stanley
 
Question 1:

Set a berakpoint in the code of the object in question. You will then be able to single-step throught the code exactly in the order in which it is executed. It makes no difference whether that code is in the object or the class or sub-class on which it is based. The debugger wil show you the code as it encounters it.

Question 2:

You have misunderstood NODEFAULT. It's doesn't prevent the parent code from being executed. Rather, it inhibits the native action of a control. For example, if you put NODEFAULT in a KeyPress event, it prevents the key press from being processed.

To prevent the parent code from being executed, place any code (even a comment) in the object or sub-class. In general, when VFP encounters a method or event containing code, it will execute the code, but not the code of the parent. In other words, the code in the object or sub-class will override the code in its parent.

If you don't want that to happen, call DODEFAULT() in the object or sub-class code at the point at which you want the parent code to be executed.

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
Hi,

In order to illustrate what Mike wrote sub 2), please have a look at the code snippet below.

Code:
goForm = CREATEOBJECT("frmForm")
goForm.Visible = .T.
goForm.Show()

READ EVENTS

CLOSE ALL
CLEAR ALL 

RETURN

**********

DEFINE CLASS frmForm as Form

AutoCenter = .T.
BorderStyle = 2
Width = 540
MinWidth = 540
Height = 300
MinHeight = 300
MinButton = .F.
MaxButton = .F.
ShowTips = .T.
Themes = .F.
Caption = "Main Form"

liValue = 12

ADD OBJECT txtBoxTwo as txtBox WITH ;
	Left = 12, Top = 36, Enabled = .T.

ADD OBJECT txtBoxOne as txtBox WITH ;
	Left = 12, Top = 12, Enabled = .T.

	PROCEDURE txtBoxOne.GotFocus()
		DODEFAULT()	&& Comment this line out in order to see the change in behavior 
		
		WAIT WINDOW + "Hi" TIMEOUT 2			
		
		This.Value = ThisForm.liValue 
		
	ENDPROC 

	PROCEDURE Destroy()
		CLEAR EVENTS
	
	ENDPROC 


ENDDEFINE 

***********

DEFINE CLASS txtBox as TextBox
	PROCEDURE GotFocus()
		This.Value = 5

	ENDPROC 
ENDDEFINE 
**********

hth

MarK
 
Ok, more questions...
There is no gray area on the left side to add a breakpoint as shown here. The top window contains the code from pressing the "View Parent Code" button. Setting a breakpoint and a "set step on" anywhere doesn't cause the debugger to traverse the parent classes code.

Screenshot_2023-03-18_144020_nkpel0.jpg


More about my style of programming...
I use the gui and 99% of the time I use "set step on" when debugging. Don't remember why I went away from "red dot" debugging as that was a long time ago, as in the early days I did use it, until something moved me to the "set step" way.

To get my subclassed objects, I created a class library containing all objects from the vfp base class. O them make custom changes to the PEMS and save the class library. Next, in VFP-Options I set the form and all other object types to use the object in my custom class. Therefore, when I create a new form or any other object, from the gui, I select new and object type and I get the expected object.

I never within code create forms or other visual objects via code, other that testing something. I do create non-visual objects such as my goApp object that has replaced most of my public vars.

I appreciate detailed answers, but they are not helping as I never use a "Define Class parentform as Form" to create a form. Instead, I click the New-Form button from the VFP toolbar and I get a new form based on the subclassed form within my vcx.

Hope this helps with my style that I've had to figure out thru trial and error over the years. Zero formal or classroom training, other than here, VFP conferences and Whil's books and I've been able to do a lot. I also know that it is very difficult to describe in words visual concepts, hence the need for long details, which in turn makes it difficult to visualize.

Chris said:
I think the more relevant question now is, what do you actually want to achieve?

Have code in the subclassed object's events that...
a. parent code executes with no code in the child object,
b. code in the child object executes AFTER the parent code executes,
c. only code in the child object executes, ignoring the parent's code,
d. run some child code, then run the parent code, then finish up with rest of child code such as shown in the screenshot where I start with "release all" in the child code then be able to call the parent code via a dodefault.
e. simply turn on or off the parent code.

Chris said:
Public variables are really public

Yes, I know, but only after being executed, and if a nodefault causes the event that sets the var as public is skipped, then it would not be created. And yes, according to everyone here, it doesn't work that way, and if it doesn't then what use is it, if it doesn't skip the default action as defined in the parent?

By the way, I have replaced almost all my global vars with either form properties or properties in a public object named "goApp" and is behaving as expected. Honestly, I really don't see the advantage of using a public goApp object, other that a lot of extra work. Its much easier in the app's main program to declare all my public vars and initialize them where they live until the app is closed. Its much easier to do a
Code:
Public gcThis, gcThat, glIsDeleted
gcThis = 'This'
gcThat = 'That'
glIsDeleted = .F.

than a
Code:
	Public goApp
	goApp = Createobject('Empty')

	* these items are global to the app
	AddProperty(goApp,'cThis', 'This')
	AddProperty(goApp,'cThat', 'That')
	AddProperty(goApp,'lIsDeleted, .F.)
	AddProperty(goApp,'cAppGuid', '{123456789}')
	AddProperty(goApp,'cAppPhoto', '.\MultiMedia\Bitmaps\appphoto.jpg')

Thanks,
Stanley
 
I deleted my posts, as I think I misunderstood your questions and intentions. And, well, they are answered by Mike in all relevant aspects, already.

So, in very short:
1. That's just normal debugger behaviour and nothing special is required.
2. You already prevent the parent class code by having code in the same event or method yourself.

Also 2: Own code always overrides parent code and that's the reason only DODEFAULT() needs to exist. Or lets say that NODEFAULT does not exist as the counterpart of DODEFAULT(). It would not need to exist for the function you thiink it has. Your own code already does what you think needs to be specifically asked by having NODEFAULT in your code. Any code already is overriding and not executing parent code.

Whereas, what still runs even though your code overrides parent class code is root native base class behaviour. To be able to suppress that the NODEFAULT command exists. So these are not two sides of the same coin. NODEFAULT is suppressing the root native baseclass code that VFP developers put into the class. That's not even VFP code. And this part of a class method or event always runs after your code and even if you write code. Therefore nothing is needed to execute it and only NODEFAULT is needed to be able to suppress it.

Last, not least, DODEFAULT() only makes sense when your inheritance chain at least has two user-defined classes of which the second inherits from the first. The first class of an inheritance chain always is one root native base class and this first class level you write in a DEFINE CLASS or in a visual VCX class always inherits from one of these native base classes VFP offers. So actually your first class already is the second link of a chain of classes that starts with the base class you pick and you add the second link. The third link is in the chain then is a class that inherits from a class based on a baseclass.

So, for example, DODEFAULT() in the keypress of a form you create from the base form class, does not call anything, there is no previous VFP parent class aside from the form base class, that has any VFP code in it to call. You don't call the base form class code, the base form class code calls itself and always runs after any of your code, you don't suppress it by writing code, you only suppress it by executing NODEFAULT. Only that makes this command necessary.

The irony of all this is, that in the end, as any code suppresses parent class code, also NODEFAULT does so. It does suppress more, though. In case there is base class behaviour you do suppress that on top of suppressing parent class code. In that sense, it is not an error to write just NODEFAULT into an event or method, but it does more than necessary and you could even just write a space or *, a comment, to suppress parent class code to execute. But you must also have a wrong expectation that code of the parent class executes if you don't suppress it by NODEFAULT. It does not. So all you need for the parent class code level is the DODEFAULT() function to be able to override your override and execute the parent class code. The only other way parent class code does execute without explicitly calling DODEFAULT() is to keep an event or method empty on your level, that expresses the wish to inherit code and run it. Otherwise, we'd need to put DODEFAULT() into any event or method we want to inherit from the parent class.

So remember: DODEFAULT() is always about the direct previous level of the inheritance chain, the direct parent class and executes what by default is not done. NODEFAULT is always only about the root (of all evil), about the native VFP base class and only suppresses what is otherwise done by default.

Chriss
 
Stanlyn said:
Have code in the subclassed object's events that...
a. parent code executes with no code in the child object,
b. code in the child object executes AFTER the parent code executes,
c. only code in the child object executes, ignoring the parent's code,
d. run some child code, then run the parent code, then finish up with rest of child code such as shown in the screenshot where I start with "release all" in the child code then be able to call the parent code via a dodefault.
e. simply turn on or off the parent code.

Actually, Mike already answered this, and my previous post, which I wrote before reading your last post.

a) that's default. If you don't write any own code, that means inheritance. You don't need to write DODEFAULT() into all methods and events you want to inherit, that would be mad, it would mean no inheritance is the default and you'd need to actively state that you wish inheritance to happen.
b) If you want that, then write DODEFAULT at the start of your code. First understand that while not writing any code means inheritance and we're saved from the madness to need DODEFAULT everywhere, anywhere you do write new code, this is overriding parent class code. Parent class code does not execute automatically after or before your code but your code runs instead of parent class code, unless you call the parent class code with DODEFAULT(). So the madness we don't have everywhere still does exist in the form of needing to express the wish to inherit parent class code and not to override it. If you write code AND you want the parent class code to also run you need DODEFAULT(). What you don't need for the opposite case is NODEFAULT, that has another meaning, see my previous post.
c) is done by having code that never does DODEFAULT().
d) Is done by putting DODEFAULT() where you want the parent code to run. You decide when the inherited code runs. Only you decide, and you could even call DODEFAULT() with other parameter values multiple times, if that makes any sense.
e) Turning off parent code is done by even just a space or comment in a method or event. If you also want to suppress base class behaviour you also need NODEFAULT, but only then. NODEFAULT only cares about the root native base class, the lower end of any VFP class inheritance chain, as all classes that don't subclass another class you or someone wrote in VFP have to inherit from one of VFPs native base classes. NODEFAULT always only is about that root end of the inheritance chain that you otherwise don't override with any code you write.

To illustrate the difference and necessities of DODEFAULT() for VFP code and NODEFAULT for native class code what you need to understand is that VFP is designed using the two completely opposite strategies of a) Native base class code is always executed after the code you write into an event or a method. So you need something that can suppress this in case you want to, you need nothing to enable this, as that is the default. Instead suppressing that is the NODEFAULT (command).
b) VFP class code is not executed after code you write in further subclass levels, nor before it. That's the totally opposite strategy of always excuting it: Not executing it. And because of this opposite strategy for the user (developer) defined classes and their code vs the native base classes code, the need for something arises, that enables you to execute parent class code, that's the DODEFAULT() function. To compensate the discomfort or disadvantage you can decide when the parent class code executes by putting the DODEFAULT() where it suits you. And that indeed is an advantage over always executing the parent class code before your code, or isn't it? You often will need the parent class code to not run first. You even expressed that wish in c) d) and e).

Chriss
 
stanlyn said:
There is no gray area on the left side to add a breakpoint as shown here...

You show the image of a code window of the parent class code. This is read only, only showing the code. If you want to edit that or even just set a breakpoint you need to close the class and instead load the parant class into the class designer.

Chriss
 
One question about your idea of how things work or should work: Let's assume NODEFAULT would exist to suppress the parent class code to run, and by default it runs before your code, then VFP would at least first need to peek into your code to see if you wish to suppress the parent code. Could be done, okay, but what about having NODEFAULT anywhere within your code in an if or else branch? It can't work the way you think it works. Or your code would first need to run to decide whether the inherited code is not suppressed, but then after that is decided the inherited code would run, as if your code didn't yet run and then your code runs again. Or how should that be done, stanlyn?

It's mad enough how it is, don't make it madder than that.

Execution order is:
1. last class inheritance level runs first.
2. parent class code runs when you don't have any code or when you DODEFAULT() in your code.
3. Then again, the parent of the parent depends on the same DODEFAULT mechanism programmed into the code of the parent class until the first user defined class based on a VFP base class. That needs no DODEFAULT, because...
4. The base class code always runs last. There is no need for DODEFAULT, but a NODEFAULT done in any class-level code of the whole inheritance chain suppresses this. So NODEFAULT only is about this base class level, not any level above it.

Does that become clearer now? It is indeed a harder to understand concept of opposite strategies used at the same time. But it has its advantages, too.
Advantage I) You decide when parent code executes and which parameters it gets with DODEFAULT(). And since you do write code into a method, it's not much asked to include this call, where it suits you. And if you want to override something and only do your code instead of what is inherited, you don't use DODEFAULT().
Advantage II) The base behaviour is most often so important that you don't want to depend on not forgetting the DODEFAULT() to run it. So the VFP developers decided for that root level we fix the execution order to do this last, but always, unless you actively suppress it by NODEFAULT.

Consequences of this are, that to execute a full chain of inheritance of code, you need to have DODEFAULT() at any place you have code other than the first class based on a VFP baseclass. Otherwise, you end the chain of inheritance and that class level then overrides what was written into classes further down the inheritance chain. You have a broken link, figuratively.

NODEFAULT, by the way, can be used on any inheritance level, as any level might want to decide when to suppress the base behaviour of the native VFP class. You don't want to depend on an unbroken chain of links to arrive in the first level class and execute NODEFAULT on that level. Instead, NODEFAULT on any level suppresses the root class behavior at the far end of the inheritance chain and only that, and this way you don't depend on the whole chain classes down to the baseclass to execute. You can have very good reasons to really override code in one event or method in the whole inheritance chain and establish a new base ground there, for that method or event. notice the immportant thing here, this does not break inheritance of other methods or events, these chains or cascades of DODEFAULT() calls can still link to parent classes in other events or methods. This is all per method and event. And in many cases calling NODEFAULT and still calling DODEFAULT(), too, does make sense, as the NODEFAULT does not decide about the parent class level, but about the root class level. And DODEFAULT only decides about the next parent level.

Chriss
 
Maybe I found what causes your "unexpected behavior": You have a RELEASE ALL in some Load event. Well, if that Load event does not run, because a subclass has Load event code not executing DODEFFAULT, then you don't RELEASE ALL.

stanlyn said:
if a nodefault causes the event that sets the var as public is skipped, then it would not be created.
First you're wrong about NODEFAULT, but doesn't matter here. If you don't execute the parent class load you also don't execute that RELASE ALL and so the public variables still exist from a previous run, not because the base class load ran.

The proof, and the only real proof, is in single stepping through the code. And please, to see what is happening don't put a breakpoint within the class code, put it in PRG before you make the CREATEOBJECT call or the DO FORM call before anything from the class or the parent class can even run. Then you see what happens, then you see which load event runs first, which runs by dodefault or which does not run at all.

Aside from that, if RELEASE ALL runs, it releases more than just variables. Have a read about it. You may step on your own foot here, once more, by using too big hammers.

Chriss
 
Thanks Chris as I think I'm understanding it better now, so to summarize

a. nodefault has no effect on any code that I write in the hierarchy. It only applies to the vfp base class objects and not any of my VFP code, at any level.

b. by adding any code in the child event, it automatically suppress the code in the same event of the parent class from executing. To include the parent code I need the dodefault() function.

c. having no code in the child event only runs the parent code.

d. having code in both the parent and child events, and needing them both to run, I need to add the dodefault() function anywhere in the child code when I need to run the parent code.

Question: Will it run it in top-down order? Because the child event has code, the execution will only start processing the child code from the top and as it works its way down and sees and runs a dodefault(), then step out and run the parent code, then return to finish the code after the dodefault?

e.

What was throwing me was nodefault() does not execute against code I write, only the non-vfp base class code. I was assuming it would suppress code I added to the parent class (not vfp base class) using the same event name, as there is no other code that I control or can change.

Your explanation has been most helpful.

Chris said:
First you're wrong about NODEFAULT, but doesn't matter here. If you don't execute the parent class load you also don't execute that RELASE ALL and so the public variables still exist from a previous run, not because the base class load ran.


Chris said:
if RELEASE ALL runs, it releases more than just variables.

Yes, I know, and it doesn't matter in this case because I was only dealing with a single object (a form) and a single event (load event) and wanted to make sure nothing else was factored in.

Another question... How does the nodefault and dodefault() apply to events and methods that has code from the base class in them? Not code that I supplied. Such as the error event where the top line is
"Lparameters nError, cMethod, nLine". Does the fact that code being there causes the parent code to be ignored as in rule #2 above?

I'm working thru this now and thanks for being persistent...

Thanks,
Stanley

 
Stanley,

Let me pick up those particular points:

a. nodefault has no effect on any code that I write in the hierarchy. It only applies to the vfp base class objects and not any of my VFP code, at any level.

That's almost right. To be exact, it applies to the built-in events, such as KeyPress, regardless of whether it is fired from a base class or a sub-class.

b. by adding any code in the child event, it automatically suppress the code in the same event of the parent class from executing. To include the parent code I need the dodefault() function.

Exactly right. And remember, "any code" includes comments and even just spaces.

c. having no code in the child event only runs the parent code.

Correct.

d. having code in both the parent and child events, and needing them both to run, I need to add the dodefault() function anywhere in the child code when I need to run the parent code.

Correct.

Question: Will it run it in top-down order? Because the child event has code, the execution will only start processing the child code from the top and as it works its way down and sees and runs a dodefault(), then step out and run the parent code, then return to finish the code after the dodefault?

Depends what you mean by "top down order". Also, the term "child" doesn't really mean anything here. Execution starts with the code in an object (that is, the actual instance of the class. If there is no code at the object level, or if the object's code contains DODEFAULT, execution continues up the hierarchy, first to the object's immediate parent, and then to its parent, and so on.

Keep in mind too that DEDEFAULT() is function. As such, it can accept parameters and return a value.

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
Me said:
And remember, "any code" includes comments and even just spaces.

This point sometimes trips people up.

You are working in the form designer (or class designer). You write code that you want to override the parent code. It's all working fine. But then you change your mind. You decide to keep the parent code after all. So you delete the code you have just written.

In this scenario, if you accidentally leave even a single space or a blank line in the code you have just deleted, the parent code won't execute.

To be completely sure of removing all the code, go to the property sheet within the designer, find the method that you are editing, right-click and select Set as Default. That will guarantee to clear out the code so that the parent code can execute. (But, be careful, there is no Undo for this operation.)

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
Stanlyn said:
works its way down and sees and runs a dodefault(), then step out and run the parent code, then return to finish the code after the dodefault?
Exactly that. Think about DODEAULT() as a call to the parent class same event or method. Even though only your class object exist, not a parent class object, all parent class code also is available, if you create an object of a class, that always makes VFP load all the class hierarchy.

It works the same way as a call of something else, it is called, runs and returns at some point. Mike already said you also get the return value, of course. So Dodefault is like calling your own objects Load event, just on the level of the parent class.

stanlyn said:
Such as the error event where the top line is
"Lparameters nError, cMethod, nLine"
Well, look into the property window. It states [Default] for the error event, or [Inherited .....] if you wrote code into the parent class error event. Even when you open it and have the LPARAMETERS in there, that actually does not count as code. That's important to add to the explanation. If you write even just a space in the next line, you'll see the Error event turns to [User Procedure], but just the LPARAMETERS won't change that. There are, again, some corner cases. If you open some ActiveX control methods and the LPARAMETERS line is generated, this can turn these methods or events from [Default] to [User Procedure]. The way to return to the default or inherited state is right click on that event or method and the "Reset to Default". Caution, if you do that in a method or event with code, that deletes that code.

The LPARAMETERS line s an exception from the overriding as the parameterization is a definition in the base or parent class, that's actually code from there, you inherit the parameterization that way. But as long as you don't touch it, it means no change and no override. So you don't have to be overcautious to never look into an event and thereby already risk to override it.

The final word is seen in the property window. If it shows [Default] or [Inherited ....] that's telling VFP regards this as not overridden. It's then also not bold. Methods and Events with code in this class are [User Procedure] in bold. So alone this style bold vs not bold tells you that overriding status. VFP makes no difference by the way, whether you have a DODEFAULT in your code or not. But it shows all methods and events that have code in this class as bold.

Chriss
 
Thanks Chris and Mike for helping me to make sense of this and is very different from what I expected. I just looked again at the help to see what I missed regarding our discussion and found nothing. Which begs the question... Where did you learn this?

Anyway, thank you for the hi-value answers in this thread.

Stanley
 
Hi Chris,

Chris said:
I deleted my posts,

Why not add them back as that would add value to this discussion now that I have a better understanding of how it all works. The posts you deleted was much more advanced and at the time made little sense. They would make a lot more sense now, so why not add them back, as it would complete the discussion for me and someone else struggling with the behavior.

Thanks,
Stanley
 
I can't add them back. They remain available by clicking the [Post Deleted] links. But you can't red flag them to ask the forum moderators to undelete them. I could add them back by repeating them. Later, though.

Chriss
 
Which begs the question... Where did you learn this?

The same way I learned most of what I know about VFP. A lot of reading, and a lot of trial and error. I always found FoxPro Advisor to be an excellent source of information, but unfortunately it is no longer available. I can also recommend the Hackers' Guide; it's quite out of date now, but the information on NODEFAULT, etc. is still valid (start with the section on DEFINE CLASS, but ignore the references to the [tt]::[/tt] operator).

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
Hello stanlyn,

you asked me to undelete my posts to add back to the discussion. I said I do so by reposting instead. And let me summarize everything I said and concentrate on what's not in the rest of the discussion since then and reinstate some code samples. So I skip some things I mentioned Mike already said and I skip some things I later posted again. And, for example, since I now know you're familiar with using SET STEP ON, I spare advice for novices in debugging.

Let me just repeat one piece of debugging advice, though: To see everything happening, you write code that does the CREATEOBJECT('formmclass') and then step through all by starting there, at the line doing the CREATEOBJECT().

stanlyn said:
"I appreciate detailed answers, but they are not helping as I never use a "Define Class parentform as Form" to create a form.
Remark at this point: As my tests turned out a whether you have a PRG or visual form classes, the behaviour turns out the same. So it is not irrelevant. I still only post the code samples and screenshots from the VCX implementation.

These are the classes:
classes_duzxrk.jpg


In the visualparentform I have code in the Load() event:
Code:
Public gcVar1
gcVar1 = 'test'

In the visualchildform based on the visualparentform I also have code in the Load() event:
Code:
On Error ? Message()
      ? gcVar1
Nothing more.

I still wrote a PRG on top of these classes that is used to start the debugging. It is
Code:
Release gcVar1
oForm = CreateObject("visualchildform")
Obviously I have SET CLASSLIB so this works. I start debugging in the CreateObject() line. The first thing the debugger shows, when I single step into the Createobject() call is:
visualchildformload_mg6e0d.jpg


What you see is that the visualchildform Load() happens first and since it doesn't call DoDefault() the parent form load does not run.
When ? gcVar1 is executed that leads the trace window to show the On Error code:
ONERROR_womnhy.jpg


The error "Variable 'GCVAR1' is not found." is put on _screen.

I see the behaviour you expect. I think your issue here is that the variable still exists from a previous run. But make your own debugging experiments to see for yourself.

At this point, I thought the more relevant question could be, what do you actually want to achieve? If you want something visible throughout the form, then a form property would be better scoped for that as a public variable.

It would be worth another question, to talk about properties. If you want a child class not to inherit a property of the parent class, it's not possible to state that in the child class, the Property/Method editor window only allows you to change the visibility of the property you inherited, from public to hidden, for example. But that does not change it to be hidden from yourself. It's then only hidden for the next levels of classes inheriting from the child class. So you may want to define a parent class property that's only visible for the parent class itself, then set this to hidden in the class definition of the parent.

If you want a property to be inherited by some child classes and not others, there is no visibility for that in VFP. Hidden means no inheritance of the property, it's only visible on that class level, public means it becomes inherited. Protected is inherited too, but access to it is only possible from code within the class family, not from the outside. Those are the inheritance possibilities of properties. And you see there is public visibility of properties, but that differs from a public variable. Same keyword, but a totally different technical meaning. Actually same natural language meaning, but a variable is still something totally different in scope than a property, a variable isn't scoped to an object.

In my last post then I picked up that idea and implemented it that way. Pardon me for still posting this as a pure code example. The point here is not whether the forms are Declared in PRG code or visual classes. The point is to demonstrate how something you actually didn't need but I thought you could have meant could be done: A property that's not existing by default and is generated in a parent class if, but only if a child class wants it by calling DoDefault() or by having no code in the Load to prevent the property creation.

Code:
Clear
On Error ? Message()
? 'creating childform1...'
oChild1 = CreateObject('childform1')
? '-------------'
? 'creating childform2...'
oChild2 = CreateObject('childform2')
? '-------------'
? 'creating childform3...'
oChild3 = CreateObject('childform3')

Define Class parentform as Form
   Procedure Load()
       This.AddProperty('cProperty','test')
   EndProc
EndDefine 

Define Class childform1 as parentform
   Procedure Load()
      DoDefault()
      ? This.cProperty
   EndProc
EndDefine

Define Class childform2 as parentform
   Procedure Load()
      * this will error
      ? This.cProperty
   EndProc
Enddefine

Define Class childform3 as parentform
   Procedure Init()
      * this will not error
      ? This.cProperty
   EndProc
EndDefine
It would be of no use, if you ask me. If a child class wants a property to exist, it could do the AddProperty() call itself. The only reason to "delegate" this task to the parent is to think of it as the responsibility of the parent class definition to provide properties necessary for the whole following tree branch of subclasses. And perhaps also to set it to some default or initial value, which could depend on the current state of the whole application and that's also the responsibility of the parent class, already.

But well, then it also is just the decision of the parent class and not of child classes, to define and to set properties. So it's really an irrelevant piece of code. You can perhaps learn from it, once more, that a method not doing the Dodefault as in the case of childform2 Load() does mean the parent method does not run. That leads to the error, as the parent Load didn't define a property the childform2 uses. Childform3 then demonstrates how you can use the property even though you did not Dodefault(). Well, because the Load() event is not overloaded and so runs. And Load() runs before Init(). Remember what I also said: An inheritance chain exists per method. So breaking one of the chains does not break all others. Childform3 breaks the Init() inheritance chain and doesn't DODEFAUL of parentform Init(). That could cause problems also if you now have no parentform.Init() code, if later the parent form has Init() code, Childform3 would still behave as if it didn't. That's often a reason you program something on a low hierarchy level and wonder why it doesn't affect some classes.

It's wise to put in a Dodefault() even if you know some method or event currently has no code. But it can also become a case of "however you do it, you do it wrong." as in some cases you might really want some branch of a family of classes to not inherit something you add later. And then, well, you change a base class and also change some subclass of it and remove a Dodefault() where it's harmful, or put it as the last line and not the first line of the subclass code. You can't foresee which order of execution is best.

I can say from my experience and gut feeling in most cases the behaviour you initially assumed, that parent code automatically runs before your class, that would be a disastrous case to not be able to decide that. I usually will do something, then call Dodefault() and then react to the parent class result of that. And you can rarely know in advance, where you want base class code to happen. I will often try to call DoDefault as soon as possible as it's also true to say that it's odd to delay what you inherited and do something yourself, first. On the other side you often declare a child class as you want it to have some different and more specific behaviour and so you often "know better" and may then even only conditionally sometimes call Dodefault(), if your class level is not the usual case of "knowing better" than you knew when programming the parent class. Well, and one extreme case of that I already said also is valid, that in some cases you know better about everything and then don't Dodefault() at all, not even conditionally. Why then not start a new base class? Well, remember inheritance is a thing per method and event.

Chriss
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top