There are two key attributes of all variables in Visual Basic that are essential to understanding their use:
Scope defines the visibility of a variable. Variables can have scope ranging from global where any procedure in the application can access the variable to local to a single procedure.
Duration defines the lifetime of a variable. Variables can exist for the life of an application or can be created and destroyed in a single procedure.
The scope of a variable determines where you can access that variable in your code. If a variable is in scope
you can read or set it's value. If it is out of scope
you will not be able to access it.
There are three types of scope for variables in Visual Basic:
- Global Scope
Global variables are in scope anywhere in your application.
- Module Scope
Module level variables are in scope anywhere within the module where they are declared.
- Local Scope
Local variables are only in scope within the procedure where they are declared.
Global variables are available anywhere in your program. Any line of code in any procedure can read or write the value of the variable. While convenient, it is considered bad programming practice to overuse global variables and some programmers (myself included) make a considerable effort to avoid them entirely.
To create a global variable, declare it in the declarations section of a standard module using the Global or Public keyword.
Module level variables are available to any code within the module where they are declared
. While using global variables is considered bad programming practice, using module level variables is not. Module level variables allow you to share data between procedures without exposing that data to every procedure in the application.
To create a module level variable, declare it in the declarations section of a module using either the Dim or Private keyword.
Local variables are only availabe to the procedure in which they are created. Local variables are the most restricted in scope - not even other procedures in the same module may read or modify local variables.
You create local variables by declaring them with the Dim or Static keyword within the body of a Sub, Function, or Property procedure.
There are a few variations from the basic scoping rules:
- Public Variables in Class Modules
Unlike public variables in standard modules, public variables in Class modules are only in scope when an object variable representing the class is in scope. For example, you may have a CPerson class with a public FirstName variable. CPerson:FirstName is public, but it is only available if there is an object variable based on the CPerson class currently in scope. You can't simply declare the variable in the class module and then reference it without first creating a CPerson object.
- Public Variables in Form Modules
Although forms can be thought of as being derived from classes, Visual Basic treats forms differently. Unlike a class, you are not required to declare form object variables (although it is considered good programming practice to do so). If you reference a public variable in a form from anywhere in your code, a copy of the form is created for you by Visual Basic. As you might imagine, this can lead to some unintended behavior if you are not aware of it.
If variables with the same name are declared so that more than one of them is within scope when the variables are referenced, the most local copy is used. For example, if you declare a variable named sName in a procedure, and also have an sName variable declared as Private in the declarations section of the same module, as well as a global sName variable declared in another module, the following rules would apply:
You can avoid the problem of name shadowing easily by using a naming convention that adds a prefix indicating the scope of a variable. A common convention is to prefix module level variables with "m" and global variables with "g". Local variables receive no prefix indicating scope.
- Within the procedure where sName is declared, the local copy of sName would be used.
- Elsewhere within the module with the private sName variable the module level variable would be used.
- Code in other modules will use the global sName variable.
Good programming practice dictates that you should try to limit the scope of your variables to the most restrictive scope you can use. If possible, data that needs to be shared between procedures should be passed in the parameter list. This is an explicit data sharing mechanism and there is no doubt about how the procedure obtains it's values.
For data that needs to be maintained independently of any procedure, module level variables are the next best choice. While module level variables are exposed within the module, they are hidden to the rest of the application. This encapsulates data that is used by a module within the module and does so privately. No code outside of the module is even aware of the existence of private module data and thus cannot alter it.
Global variables should generally be avoided. While they are convenient for sharing data between modules, they are as dangerous as they are easy. Because any procedure anywhere in the application can change a global variable, you may have code changing globals in unexpected ways - leading to unpredictable behavior in the application and extremely difficult debugging.
Don't, however, confuse global variables with global constants. Just as it is advisable to avoid the use of global variables, it is recommended to use constants wherever possible. Since constants cannot be changed at run-time, the problems of global variables are avoided. The primary purpose of constants is avoiding "magic numbers." Magic numbers are values like coded property settings. For example, a combo box can be assigned one of three styles: dropdown combo (a normal text box with a drop down list), simple combo (the list is always displayed), and dropdown list (the user must choose a value in the list). These styles have the values 0, 1, and 2 respectively. Instead of using the actual values, using symbolic constants adds a level of abstraction to the code. For the combo box, VB supplies the intrinsic constants vbComboDropdown, vbComboSimple, and vbComboDropdownList. Using one of these constants makes the code easier to read because an English-like word replaces an arbitrary number.
Variable duration, or lifetime, indicates how long a variable exists in the life of a program. A variable may have a duration ranging from as long as the lifetime of an application or as short as the lifetime of a single procedure. Duration is closely related to variable scope because the location where a variable is declared can affect both.
Normally, all variables declared at the module level have a duration of the lifetime of an application, and variables declared within a procedure only exist while the procedure is executing. There are, however, a few exceptions to this general rule.
- Static Variables
You can declare local variables (variables declared within a procedure) or even an entire procedure as Static. Static variables retain their values between procedure calls. A common use of static variables is as control flags to prevent unbounded recursion (a recursive function is a function that calls itself - if not controlled, a recursive function can quickly consume all of the available space on the stack, leading to a stack overflow error), control flags for write-once property settings, and so on.
- Class Modules
Module level variables declared in class modules exist for the lifetime of the class objects. Remember that with classes you cannot directly access code or data within the module without first creating an instance of an object defined by the class.
- Form Modules
Form modules, like class modules, require that an instance of the form be created before its code and data can be used. However, VB treats form modules somewhat differently and will automatically create an instance of a form if any propery of the form is referenced in your code. Also remember that module level variables in forms are not destroyed until the reference to the form object is released by setting the form to Nothing. Unloading a form does not clear the value of the form's module level variables.
Key concepts to remember about variable scope and duration are:
- Variable scope determines where in your code you can "see" a variable.
- Variable duration indicates how long a variable exists in the lifetime of a program.
- VB's rules for shadowing state that the most local copy of a variable will be the copy used.
- Special rules apply for variables in form and class modules that affect both the scope and duration of the variables in the form or class.
- Good programming practice dictates that you should try to limit the scope of your variables to the smallest range possible.