NODEFAULT, DoDefault, Who’s Got ‘da’ Fault?

Jim Booth

Visual FoxPro provides for inheritance. Inheritance is the incorporation of data and behavior into one class from another class. In VFP, inheritance is by exception–that is, the behavior is inherited unless the subclass overrides it. Any code at all in an inherited method will override the superclass (parent class) behavior completely. Well, almost completely. In this column, Jim Booth investigates when and where code is overridden and what we can do to incorporate or eliminate behaviors from our subclasses.

Visual FoxPro comes to us with a set of base classes. All classes we design must originate from one of the VFP base classes. If we create a command button class, it must be a subclass of the VFP CommandButton base class. Once the new button class is designated to inherit from the CommandButton base class, the new button will have all of the properties, methods, events, and behavior that the CommandButton base class has. This is inheritance, and VFP supports it.

Take this

For the sake of our discussion, let’s assume that we have a text box class (named MyText) inheriting from the VFP TextBox base class. If we place MyText in a form and run the form, we can type in the text box, and what we type is displayed in MyText. We see the characters typed because the default behavior, in the VFP TextBox base class KeyPress event, is to process the keystroke and display it in the text box. This behavior is the default behavior of the VFP base class. We can create a subclass of our text box class (named MyText2), and it will exhibit the same behavior because the behavior is being inherited from the TextBox base class to MyText and then to MyText2.

A step further

Let’s return to the MyText class and write some code in the KeyPress. The following code segment is what we’ll put in the KeyPress event of MyText:

* MyText KeyPress
LPARAMETERS nKeyCode, nShiftAltCtrl

IF UPPER(CHR(nKeyCode)) = "A"
  WAIT WINDOW "No A's Allowed"
ENDIF

Now create a form with MyText2 in it and run the form. When you type an A in MyText2, you’ll see the WAIT WINDOW message even though you didn’t write the code in MyText2. That’s because the behavior is inherited from MyText. If you now edit the MyText2 class and open the KeyPress, you’ll see that the code isn’t displayed. That’s because the code isn’t inherited by the subclass; the behavior is inherited.

Now for some fun

VFP implements an inheritance model called program by exception. Program by exception means that a subclass will inherit behavior from its superclass (parent class) as long as no code has been written in the subclass’s event or method being inherited. If one line of code (even a comment) is written in the event or method of the subclass, then the inheritance of the behavior of the superclass is severed.

To prove this, add the following code in the KeyPress event of MyText2 class:

* MyText2.KeyPress
LPARAMETERS nKeyCode, nShiftAltCtrl
* Testing the program by exception

Now run the form with MyText2 again, type A into the text box, and you’ll see that the WAIT WINDOW doesn’t get displayed. This is because the comment you added to MyText2.KeyPress has severed the inherited behavior from MyText.

But wait, didn’t we see the A echoed in the text box? Yes, we did. Isn’t the behavior of displaying the characters part of the text box base class’s default behavior? Yes, it is. Then why did it get inherited if we severed the inheritance by adding code?

The answer is that the default behavior of a VFP base class is handled differently. It’s always inherited, and it automatically executes after any code you add to the event or method.

One at a time

First, let’s focus on the MyText behavior. How do you handle the situation where, in a subclass, you need to add behavior and also retain what would be inherited? VFP gives us two methods for forcing the superclass’s behavior to execute: 1) the DoDefault() function; and 2) the scope resolution operator (::).

The DoDefault() function calls the same method in the superclass and executes the code there. Let’s modify the code in MyText2.KeyPress to be as shown here:

* MyText2 KeyPress
LPARAMETERS nKeyCode, nShiftAltCtrl
* Testing the program by exception
DoDefault()

Run the form again, and type in the text box. This time an error occurred–why? It’s simple, really–the KeyPress event of MyText2 ran, and it received two arguments from VFP: nKeyCode and nShiftAltCtrl.

Then the comment severed the inheritance. Finally, the DoDefault() called the superclass’s KeyPress event. However, the DoDefault() didn’t pass the two arguments, so MyText.KeyPress did what VFP does when a parameter is omitted from the call–it defaulted them both to .F.. When the line IF UPPER(CHR(nKeyCode)) was encountered, it found nKeyCode is a logical and CHR() wants a numeric; hence the error. First lesson: If you use DoDefault(), be sure to pass the parameters back to the superclass.

Fix the code in MyText2 by adding the two parameters–DoDefault(nKeyCode, nShiftAltCtrl)–and run the form again. The error is gone.

What about the scope resolution operator–how does that work? Modify the code in MyText2.KeyPress as shown here:

* MyText2 KeyPress
LPARAMETERS nKeyCode, nShiftAltCtrl
* Testing the program by exception
MyText::KeyPress(nKeyCode, nShiftAltCtrl)

Run the form again, and you’ll see the same behavior as with the DoDefault(). Which one should we use? This is a matter of opinion, but I say we should use DoDefault(). Why? Because DoDefault() always runs the code in the class immediately above the one with the DoDefault(), and this is how classes should behave. A subclass-to-superclass relationship can be called an "is a" relationship–that is, MyText2 is a MyText, which, in turn, is a TextBox. The scope resolution operator allows you to call to any class in the hierarchy by explicitly naming the class on the left of the operator. While this might seem to be more flexible, it allows for very poor class design.

If you have a subclass and, for whatever reason, you want to call inherited behavior bypassing the immediate superclass, then your MyText2 is probably not a MyText. In other words, MyText2 should probably be a subclass of MyText’s superclass and not of MyText itself. If DoDefault() doesn’t get you what you want, then you should review the class design and not succumb to using the scope resolution operator to get around a poor design.

Next

Okay, so we can make the superclass behavior execute even if we add behavior in a subclass, but what if we don’t want the base class’s behavior to execute? VFP gives us the NODEFAULT command for this purpose. NODEFAULT stops the execution of inherited behavior including the VFP base class default behavior.

Change the code in MyText2.KeyPress to this:

* MyText2 KeyPress
LPARAMETERS nKeyCode, nShiftAltCtrl
* Testing the program by exception
NODEFAULT

Run the form and type in the text box. You’ll see nothing show up in the text box because you’ve stopped the VFP base class behavior of processing the keystroke.

Now for something completely different

A silly demonstration, true, but it’s a clear one. Let’s go a step further. Edit the MyText class, and in the properties sheet, right-click the KeyPress event and choose Reset to default. Now run the form, and type in the text box (watch carefully exactly what happens and when). Notice that when you type A in the text box, you first see the WAIT WINDOW message, and then you see the A in the text box. How can you make it work the other way around–if you want to see the A first and then see the WAIT WINDOW? Edit MyText and include the following code in the KeyPress event:

* MyText KeyPress
LPARAMETERS nKeyCode, nShiftAltCtrl

DoDefault(nKeyCode, nShiftAltCtrl)

IF UPPER(CHR(nKeyCode)) = "A"
  WAIT WINDOW "No A's Allowed"
ENDIF

NODEFAULT

Run the form and type in the text box. This time, you’ll see the A in the text box first, and then the WAIT WINDOW appears. DoDefault() doesn’t only execute the superclass’s behavior, it also executes the base class behavior.

Remove the NODEFAULT at the end of this code and run the form again. Type A once in the text box. What did you see? Each time you dispatched the WAIT WINDOW, the letter A was again displayed in the text box–the display was doubled. Why? Because the DoDefault() executed the base class default behavior of processing the keystroke and displayed the A, then the WAIT WINDOW appeared. When you dispatched the WAIT WINDOW, the base class behavior executed again. VFP’s base class behavior always executes after your code unless you do something to stop it (NODEFAULT).

So what does this all mean?

VFP implements programming by exception for behavior inherited from your classes. The default behavior of the VFP base classes aren’t programming by exception; rather, you need to actively stop the base class behavior using NODEFAULT.

DoDefault() and the scope resolution operator can be used to force the execution of the behavior in a superclass that would otherwise have been severed. DoDefault() also causes the VFP base class behavior to execute. The VFP base class default behavior automatically executes after your class code finishes. So if you use DoDefault() during your code, you’ll likely need NODEFAULT at the end of the code to prevent the base class behavior from executing twice.

Jim Booth is a Visual FoxPro developer and trainer. He has spoken at FoxPro conferences in North America and Europe. Jim has been a recipient of the Microsoft Most Valuable Professional Award every year since it was first presented in 1993. He’s co-author of Effective Techniques for Application Development and Visual FoxPro 3 Unleashed and is contributing author for Special Edition Using Visual FoxPro 6.0. Jim is also the technical editor for Database Design for Mere Mortals. Visit his Web site at www.jamesbooth.com. 203-758-6942, jbooth@jamesbooth.com.