Variables and Properties:

How and Why.
by Dan Howard 


 


Introduction
A very common source of confusion in Visual dBASE is the use of properties over variables. In this article, Iíll attempt to clarify the dos and doníts of variable usage.

For the purpose of this article, when I use the term function, I mean a procedure, function or method. When I use the term property Iíll be generally referring to a custom property.

Also, most of the code samples were written in dBASE 7.01 but theyíll need only minor modifications to run under 5.7.

What are Variables?

Types of Variables

There are 4 basic types of variables: public, private static and local. Each type has itís own scope and lifetime.

Terms:

The following table summarizes the variable types and their different scopes and lifetimes. I also include whether or not you can use the macro ( & ) operator with them (See Macros for more information) :
 
 Type  Scope  Lifetime  Macro
 public Available to entire application Until end of application  Yes
 private Available only in the function where it was declared and any sub-functions. Until end of function  Yes
 static Available only in the function where it was declared. Until end of application  No
 local Available only in the function where it was declared. Until end of function  No
Declaring Variables
Itís usually a good idea to declare any variables that you use. Visual dBASE wonít complain if you donít but the default will be PRIVATE which may not always be what you want. This is how I normally declare my variables:
 
 
local a,b,c
store 0 to a,b,c
   

The reason I do it this way is so I can take advantage of the :=operator that was introduced with Visual dBASE 7. The:=is an assignment only operator. Once your variables are declared and assigned a value you can use :=exclusively to prevent the accidental creation of new variables.  This works for properties too.

Note that it doesnít matter what you initially store to the variable. As long as you put something (even NULL) into the variable, you can use the := operator.

Also, unlike most languages, dBASE allows you to declare variables anywhere inside your functions. For example, the following is perfectly valid:
 
 
Function MyVariables
   local j
   for j = 1 to 10
      ? j
   next j

   private c
   c = "dan"
   ? c
return

   

Although dBASE wonít complain about this, you should generally declare your variables at the top of the function because youíll have an easier time debugging your applications.

Using Variables
Here are examples of each variable type and how they are used:

Local

Locals are probably the most common type of variable that you would use.  Locals can be used for anything that you would use a variable for except for the macro (&) operator and the type() function.
 
 
Function MyFunction
   local j
   store 0 to j

   private k
   store 0 to k
   ? type("j")  // Will always return "U" 
   ? type("k")  // Will return "N"
return

   

Private

I generally reserve privates for cases where I need to use the macro operator or the type() function. This is simply my personal preference. You can certainly use privates in any circumstance where locals are used but you should be aware that those variables could be changed by a lower level sub-routine. For me, changing a variable in a function where it was not declared in is bad programming practice because it can make debugging more difficult. An example of where I would use privates would be:
 
 
Function Pushbutton1_onClick
   private o,m_macro

   m_macro = "form.entryfield1"
   o = &m_macro.
   if type("o.dataLink") = "O"
      ? "The datalink to this control is a field object."
   endif
return

   

In fact, you can avoid needing privates for the type() function by using this little function instead:
 
 
// Call this function like the type() function except don't use quotes. e.g.
// if LocalType(o.dataLink) = "O"
//    ?"The datalink to this control is a field object."
// endif

Function LocalType
param xValue
return type('xValue')

   

So really you only need privates for macros.

Static

Static variables are probably the least common of the variable types. They are a hybrid of publics and locals. Like locals, they can only be used in the function that they were declared in but they are like publics in that they retain their values until you quit dBASE (or do a CLEAR MEMORY). Look up the stopwatch example in the on line help for an idea on their use (see STATIC in the help index). Statics are interesting in that they are the only variable which can have an in-line assignment. That means that they can be declared and assigned at the same time. e.g.
 
 
// Increment a counter every time I get called.
Function HitCounter
   static n = 0  // This line will only run the 1st time this function is called.
   n++  // or n = n + 1 for dBASE 5.x
return ( n )
   

Statics are useful in that they are only accessible by their function. This feature lets you use statics for variables that you want to ensure are initialized correctly. e.g.
 
 
// Function INIFilename
// Returns the current INI file in use or sets
// the current INI file but only if the file
// already exists.
Function INIFileName( cFile )
   static cIniFile = ''

   // If I was passed a parameter
   if argcount() = 1
      if not file( cFile )
         msgbox('Cannot set ' + cFile + ', it doesn't exist!')
      else
         cIniFile := cFile
      endif
   endif
return ( cIniFile )

   

Statics are not all that useful in an environment like dBASE since much of their functionality is available through OOP techniques. I wonít fault you if you donít use them. ;-)

Public

Publics are variables which are global. You can reference them from any routine in your application. They are declared like this:
 
 
public a
a = 10
   

Publics appear to be the most controversial of the variable types. There has been much discussion in the newsgroups concerning whether to use them or not. Publics are useful for application specific information e.g.

One of the problems with publics though is that you can declare them in any routine. This can make debugging your code very difficult. Because of this you should only declare publics in your main program.

Instead of using public variables you can also use the _app object. The _app object is a Visual dBASE object which represents the current running application. Since itís global, it makes a good place holder for values that you want to be available everywhere. To use the _app object, simply assign the values to it like this:
 
 
_app.MyValue = 10

// or

_app.Ini = New Ini("MyIni.INI")

   

Publics are absolutely not useful for circumventing the object model. Iíll get to this in the properties section.

Macros and Indirection
Macros are strings of programming code that can be compiled and executed at runtime. e.g.
 
 
m_macro = "form.entryfield1"
o = &m_macro.
// this is translated on the fly by dBASE to:
// o = form.entyfield1
   

In many cases we used macros for dBASE commands like:
 
 
m_macro = "customer.dbf"
use &m_macro.
   

We can also do these things using the indirection operators. When you put these brackets around a variable, youíre telling dBASE to give you itís indirect value. Hereís an example:
 
 
****
* Macro Method****
****
private cTable
cTable = GetFile( "*.dbf" )
if not empty(cTable)
   use &cTable.
   browse
endif

****
* Indirection Method
****
local cTable
cTable = GetFile( "*.dbf" )
if not empty(cTable)
   use ( cTable )
   browse
endif

   

Using indirection has a few advantages. Namely that you can do these things with local variables which will shave a millisecond or 2 off of your execution time.

Indirection will not work in all places where you would use a macro but it does work with commands like: copy to, append from, use, cd, md, etc.

Variables and Fields
Consider the following code:
 
 
private first_name, last_name
first_name = "Dan"
last_name = "Howard"
?"Values to save: ", first_name, last_name
use contacts
append blank
replace first_name with first_name, last_name with last_name
?"results:",first_name, last_name
use
   

What do you think the output will be? The answer is that the first_name and last_name fields will be left blank! The reason is that when you open up a table dBASE creates variables representing the fields in the table. The fields will take precedence. One work around is to name your variables differently from the field names. This may not always be feasible. A better approach is to use the alias ( -> ) operator. Using this operator tells dBASE explicitly which variable or field youíre talking about. There are 2 ways to improve the above example:
 
 
replace first_name with M->first_name, last_name with M->last_name
// An even better way...
replace CONTACTS->first_name with M->first_name, CONTACTS->last_name with M->last_name
   

Both methods will work but I like the second one is better because it avoids any confusion as to whatís a variable and whatís a field. Notice that M is a special alias which tells dBASE that it refers to a memory variable.

Parameters
Parameters are variables that you pass to a function. There are two ways to declare parameters for a function:
 
 
Function MyFunction
parameters a,b,c
   

Or
 
 
Function MyFunction(a,b,c)
   

The difference in the two styles of declarations is that in the first example the parameters will be created as private ó in the second example theyíll be local.

There are also 2 ways to call your function with parameters:
 
 
MyFunction(a,b,c)
   

Or
 
 
DO MyFunction WITH a,b,c
   

In addition, there are 2 ways that parameters are passed to a function: by reference or by value.

From the On-line Help:

Note: The default behavior of dBASE is to pass by reference. If youíre coming to dBASE from another language, this can really bite you!
Unless you want a subroutine to be able to modify your parameters you should always pass by value. Since this is not the default dBASE behavior, you have to use the indirection operators to accomplish this. e.g.
 
 
MyFunction( (a), (b), (c) )
   

Another way to prevent accidental changes to your parameters is to always make a copy of them in the called subroutine. e.g.
 
 
do MyFunction with a,b,c

Function MyFunction(a,b,c)
   local x,y,z

   x = a
   y = b
   z = c

   // Now you can modify x,y,z to your hearts content!
return

   

Note: Objects and Arrays are always passed by reference.

Sometimes itís desirable to pass by reference. Letís say you want a function which will double whatever value you send to it. Easy enough to write one.
 
 
n = 10
Double(n)
? n

Function Double(x)
   x = x * 2
return

   

By the way, did you know that you can easily write functions which take a variable number of parameters? Itís very easy to do with the argcount() and argvector() functions. Copy & paste the following code and experiment with passing any number of parameters you want to the function:

// Visual dBASE 7.x Version
 
MultiParam("45",17,true,date())

Function MultiParam
   local j, n 
   private xParam

   n = argcount()
   ? "You passed " + n + " parameters to me!"
   ? "They're values are:"

   for j = 1 to n
       xParam = argvector(j)
       ? "Parameter: " + j + " is " + xParam + " - it's type is: " + type('xParam')
   next j
return

   

* Visual dBASE 5.x Version

 
MultiParam("45",17,.t.,date())

Function MultiParam
   local j, n 
   private xParam

   n = argcount()
   ? "You passed " + rtrim(ltrim(str(n))) + " parameters to me!"
   ? "They're values are:"

   for j = 1 to n
      xParam = argvector(j)
      ? "Parameter: " + rtrim(ltrim(str(j))) + " is "
      ?? xParam
      ?? " - it's type is: " + type('xParam')
   next j
return NULL

   

Very nifty feature ó don't you think? This also works within a class/endclass.
 
 
m = new MultiParam("45",17,true,date())

class MultiParam
   local j, n 
   private xParam

   n = argcount()
   ? "You passed " + n + " parameters to me!"
   ? "They're values are:"

   for j = 1 to n
       xParam = argvector(j)
       ? "Parameter: " + j + " is " + xParam + " - it's type is: " + type('xParam')
   next j
endclass

   

What are Properties?
After covering so much on variables itís time to examine properties and see how they differ in use and functionality from variables.

For the sake of this article, when I use the term property I mean custom property. Custom properties are the properties that you add to any of the built-in objects in Visual dBASE. Hereís a simple example:
 
 
Class MyForm of Form

    this.isOpen = false

    Function OnOpen
       this.isOpen := true
    return

    Function OnClose
       this.isOpen := false
    return

endclass

   

The question though is: Why use properties at all? The answer is because of scoping and lifetime.

Properties act pretty much the same as variables do except that they are scoped to an object and not to a function. They will exist and be accessible as long as the object exists. In the above example, the IsOpen property does not die at the end of the OnOpen() method. Itís stays alive until the form itself is released from memory. This is a very important concept to understand since most of dBASE and Windows itself is object oriented.

The advantage of a property is that you can create multiple objects and each will have itís own set of data so you never have to worry about one value overwriting the other. e.g.
 
 
f1 = new MyForm()
f2 = new MyForm()
f2.Open()
? f1.isOpen
? f2.isOpen
   

Both objects have the same property but each can contain different values.

Properties are actually a little simpler than variables because there are really only two types: regular properties and protected properties.

The property in the example above is a regular property. That means that itís accessible from anywhere as long as you have a reference to the object that itís a part of. e.g.
 
 
f = new MyForm()
f.Open()
? f.IsOpen
   

Protected properties on the other hand are hidden to anything outside of the class itself. Protecting properties is a good way to hide the internal workings of your objects and to prevent them from being changed by some other object.

Making a protected property is easy. All you need to do is use the protect keyword. Let me change my example:
 
 
Class MyForm of Form
   PROTECT isOpen
   this.isOpen = false

   Function OnOpen
      this.isOpen := true
   return

   Function OnClose
      this.isOpen := false
   return

endclass

   

If you tried this code again:
 
 
f = new MyForm()
f.Open()
? f.IsOpen
   

Youíd get an error: ďProperty is not accessibleĒ

Protecting it makes it inaccessible to anything but the class itself. That means you could only use the IsOpen property within the class/endclass statements. The property also wonít appear in the inspector.

Thereís one small problem with the above code ó namely that the Form designer doesnít stream out the protected keyword. If you were to design this form the PROTECT isOpen line would be removed next time you saved. Fortunately thereís a nifty solution to that. Try the following code:
 
 
f = new MyForm()
f.Open()
inspect(f)

Class MyForm of Form

   Function OnOpen
      PROTECT isOpen
      this.isOpen = true
   return

   Function OnClose
      this.isOpen = false
   return

endclass

   

Notice that Iím able to protect a property any time I want. In fact, if you want you can dynamically protect a property. Run this code then click on the form to see a second Inspector pop up. In the second Inspector window the isOpen property is suddenly hidden! Very cool indeed!
 
 
f = new MyForm()
f.Open()
inspect(f)

Class MyForm of Form

    Function OnOpen
        this.isOpen = true
    return

    Function OnClose
        this.isOpen = false
    return

        Function onLeftMouseUp
           protect isOpen
           inspect(this)
        return
endclass

   

O.K., enough with the funny experimentation. Letís move on. ;-)

Creating Properties
Properties can be created in any number of ways. dBASE uses a dynamic object model which means that objects can be extended after they are created. This means that new properties can be added to an object at any time. e.g.

Hereís an example of using a custom property for a form.
 
 
f = new ConfirmForm()
f.mdi = .f.
f.Prompt.Text = "Are you really really really REALLY sure you want to do this????"
f.ReadModal()
if f.YesClicked
   ? "You Clicked YES!  Formatting the hard drive...."
endif
CLASS confirmFORM OF FORM
   this.AutoCenter = .T.
   this.Text = "Please confirm"
   this.Left = 54.166
   this.Top = 7.4697
   this.Height = 7.5879
   this.Width = 59.166

   DEFINE TEXT PROMPT OF THIS;
       PROPERTY; 
         Alignment 10,;
         Text "Are you really really really sure you want to do this????",;
         FontSize 12,;
         Left 1,;
         FontBold .F.,;
         Top 0.9404,;
         Height 4.7061,;
         Width 56

   DEFINE PUSHBUTTON YESBUTTON OF THIS;
       PROPERTY; 
         Text "&Yes",;
         Left 2,;
         FontBold .F.,;
         Top 6.1172,;
         OnClick CLASS::YESBUTTON_ONCLICK,;
         Group .T.,;
         Height 1.1172,;
         Width 11.833

   DEFINE PUSHBUTTON NOBUTTON OF THIS;
       PROPERTY; 
         Text "&No",;
         Left 46,;
         FontBold .F.,;
         Top 6.1172,;
         OnClick CLASS::NOBUTTON_ONCLICK,;
         Group .T.,;
         Height 1.1172,;
         Width 11.833

   Function ReadModal
      * I'll add my custom property here.
      form.YesClicked = .f.
   return Super::ReadModal()

   Procedure YESBUTTON_OnClick
      form.YesClicked = .t.
      form.Close()
   return

   Procedure NOBUTTON_OnClick
      form.Close()
   return

ENDCLASS

   

In this example I used the ReadModal() method of my form to create a custom property called YesClicked. Notice that because itís a property, I can access it from any method in the class.

Normally, you can create these custom properties in the constructor part of the class but in the case of the form I use the Open() or ReadModal() methods so that the form designer will keep my code.

Using Properties
Custom properties are especially useful when you need to share information with different functions inside your class. e.g.
 
 
Class MyForm of Form

   Function onOpen
      form.recordSaved = false
   return

   Function SaveButton_onClick
      form.rowset.save()
      form.recordSaved := true
   return

   Function canClose
      local lCanClose
      lCanClose = true

      if not form.recordSaved
         msgbox("Please save the record first!")
         lCanClose := false
      endif 
   return ( lCanClose )
endclass

   

You could use a public for this. e.g.:
 
 
Class MyForm of Form

    Function onOpen
       public recordSaved
       recordSaved = false
    return

    Function SaveButton_onClick
       form.rowset.save()
       recordSaved := true
    return

    Function canClose
       local lCanClose
       lCanClose = true

       if not recordSaved
          msgbox("Please save the record first!")
          lCanClose := false
       endif 
    return ( lCanClose )
endclass

   

But whatís wrong with this code? Nothing really. It will work if your application only has one form. What would happen if your users wanted an MDI (Multiple Document Interface) application? What if they wanted to be able to open 4 or 5 copies of the same form to view / edit? You would be in deep do-do. Each form would overwrite the others recordSaved value. Youíd never be sure which form you were talking about!

The above code is a pretty simple example but this is what I meant by circumventing the object model.

Common Gotchas
In keeping with Peter Rorlickís dQUIZ idea, Iíll show you some code examples. Try to guess what the problem is with each example before looking at the answer. Donít cheat! :)

Gotcha #1

Look at the following example and guess what the output will be:

* Gotcha # 1
 
a()
function a
   ? program()
   for j = 1 to 10
      ? j
      b()
   next j
return

function b
   ? program()
   for j = 1 to 10
      ? j
   next j
return

   

Answer #1

Gotcha #2

Look at this code and guess what the output will be: ( this oneís easy )

* Gotcha #2
 
local object1, object2

object1 = New MyObject()
object1.SetValue(10)

object2 = New MyObject()
object2.SetValue(20)

?object1.GetValue()
?object2.GetValue()

Class MyObject

   public nValue

   Function SetValue(n)
      nValue = n
   return

   Function GetValue
   return ( nValue )
endclass

   

Answer #2

Gotcha #3

Copy & paste the following code. What do you think the results will be?
 
 
local string, x
string = "Dan Howard"
? "Original Value of STRING: " + string
x = upper(string)
? "Value of STRING after calling upper function: " + string
x = MyUpper(string)
? "Value of STRING after calling MY upper function: " + string

Function MyUpper(cStr)
   cStr = upper(cStr)
return (cStr)

   

Answer #3

Gotcha #4

What do you think will happen in the following code?
 
 
o = new MyObject()
o.Display()

Class MyObject
   private hello
   hello = "hello"

   Function Display
      ? hello
   return null
endclass

   

Answer #4

General Tips
Here are few guidelines I try to live by. They can help make your programming time easier so youíll spend less time in the debugger.

Conclusion

Well I hope I gave you enough to think about. The things to remember are to:
Happy Computing!

Thanks to Peter Rorlick for some additions.

Dan Howard has been an independent software developer for almost 10 years using Clipper for DOS and Visual dBASE for Windows.  He was recently knighted a dBVIPS member and he recently did receive his T-Shirt which he as worn ever since so it smells funny now. He can be contacted at: sproket_dBulletin_@total.net (take off ď_dBulletin_Ē).