Directives

[Previous] [ Next ]

6.1 Define focused variables .

When you create and use variables, you should strive to make each variable serve a clearly defined purpose. It can be tempting to make variables serve double duty, but the small savings in memory is often negated by the resulting complexity of the code. A variable used for multiple purposes is said to be unfocused. If you need to perform unrelated actions or calculations on a variable, you'd be better served by having two focused variables rather than one unfocused variable. It's unnecessarily difficult to read and debug code that contains unfocused variables, and the code itself is more likely to contain errors.

Incorrect:

  PublicSub  AssignGroup(strGroup  AsString  )  '*Purpose:Addagrouptothecurrentaccount. '*Accepts:strGroup-Grouptoassigntoaccount.   OnErrorGoTo  PROC_ERR  Dim  strTemp  AsString   Const  c_DuplicateRecord=3022 strTemp="INSERTINTOtblAssignedAccGroups(AccountNumber,"&_ "[Group])VALUES("&Me.AccountNumber&_ ","""&strGroup&""");"  '*Captureanyerrors-specificallyduplicaterecorderrors.   OnErrorResumeNext  Err.Clear dbGroups.ExecutestrTemp,dbFailOnError  '*Checktoseewhethertherewasaduplicaterecorderror.   If  Err.Number=c_DuplicateRecord  Then   '*Thisisaduplicateentry;ignoreandgetout.  strTemp="Thegroupyouhavespecified("&strGroup&_ ")alreadyexistsinthedatabase." MsgBoxstrTemp,vbInformation  EndIf   OnErrorGoTo  PROC_ERR PROC_EXIT:  ExitSub  PROC_ERR:  Call  ShowError(Me.Name,"AssignGroup",Err.Number,Err.Description)  GoTo  PROC_EXIT  EndSub  

Correct:

  PublicSub  AssignGroup(strGroup  AsString  )  '*Purpose:Addagrouptothecurrentaccount. '*Accepts:strGroup-Grouptoassigntoaccount.   OnErrorGoTo  PROC_ERR  Dim  strSQL  AsString   Dim  strMessage  AsString Const  c_DuplicateRecord=3022 strSQL="INSERTINTOtblAssignedAccGroups(AccountNumber,"&_ "[Group])VALUES("&Me.AccountNumber&_ ","""&strGroup&""");"  '*Captureanyerrors-specificallyduplicaterecorderrors.   OnErrorResumeNext  Err.Clear dbGroups.ExecutestrSQL,dbFailOnError  '*Checktoseewhethertherewasaduplicaterecorderror.   If  Err.Number=c_DuplicateRecord  Then   '*Thisisaduplicateentry;ignoreandgetout.  strMessage="Thegroupyouhavespecified("&strGroup&_ ")alreadyexistsinthedatabase." MsgBoxstrMessage,vbInformation  EndIf   OnErrorGoTo  PROC_ERR PROC_EXIT:  ExitSub  PROC_ERR:  Call  ShowError(Me.Name,"AssignGroup",Err.Number,Err.Description)  GoTo  PROC_EXIT  EndSub  

6.2 Give variables descriptive names .

The names you choose for variables are very important. Even if your code will never be seen by another person, you should write it as if it will be reviewed by dozens of other developers. Don't use the names of friends or pets, and don't use offensive words. When you're coming down from a caffeine high at three o'clock in the morning, you might be tempted to use all sorts of strange variable names. Using a bizarre reference to the Vincent Price movie you've been watching might help you release tension, but it will make your code considerably less readable.

NOTE
It's easier to name a variable that has been created for a well-defined purpose.

Giving your variables logical names is one of the easiest things you can do to make your code more understandable. Variables such as x , y , and i have almost no place in modern software development. They might be fine for loop counters (although better names are almost always available), but they should never hold important data. Not convinced? Have you ever seen code like this?

  Dim  rstCustomers  As  Recordset  Dim  i  AsInteger   '*PrintalistofthefieldsintheRecordset.   For  i=0  To  rstCustomers.Fields.Count-1  Debug.Print  rstCustomers.Fields(i).Name  Next  i 

Sure, i works in this example. However, what does i represent? In this instance, it represents an index of a field. With that in mind, consider how much more readable this code is:

  Dim  rstCustomers  As  Recordset  Dim  intIndex  AsInteger   '*PrintalistofthefieldsintheRecordset.   For  intIndex=0  To  rstCustomers.Fields.Count-1  Debug.Print  rstCustomers.Fields(intIndex).Name  Next  intIndex 
NOTE
You should reserve x, y, and z for variables that hold coordinates. For instance, it's perfectly valid to have variables named x and y in a charting program.

For a more dramatic example, look at the next code snippet, which was taken directly from a well-known Visual Basic publication. I've left out the declarations of the variables on purpose.

 av=Split(sCmdLine,".,"&sCRLF&sTab)  ForEach  v  In  av s=s&v  Next  

Can you tell exactly what this code does? I can't. It's pretty obvious that this code would be much more understandable if the variables had descriptive names. Also, note the use of sCRLF and sTab (constants defined by the author). Using constants in place of magic numbers is good practice, but Microsoft Visual Basic includes built-in constants for these values ( vbCrLf and vbTab , respectively). You should always use a system constant if one is available (as discussed in Chapter 5, "Using Constants and Enumerations").

An important thing to remember when you name variables is that the limitations that existed in older languages are gone. For instance, some languages limited the number of characters you could use to define a variable, so some developers still use variable names such as EMPADD1 , EMPADD2 , and STXTOTAL. Code consisting of deliberately shortened variable names is difficult to understand and maintain. There is no longer a valid reason for this practice. This is not to say that all variables should have long names; you should choose names that make the most sense ”whether they're short or long. The advantage of naming variables EmployeeAddress1 , EmployeeAddress2 , and SalesTaxTotal is clear: these names are more self-explanatory than their shorter counterparts.

Practical Application

6.2.1 Avoid naming variables Temp . One variable name you should avoid is Temp . It's easy to create variables such as rstTemp , intTemp , and strTemp , but all local variables are temporary by nature, so naming a local variable Temp is redundant. Generally , developers mean "scratch variable" when they say "temp variable," and you can almost always find an alternative for Temp . For instance, when you use the InStr() function to determine the location of a string within a string, it might be tempting to use a statement such as this:

 intTemp=InStr(strBeginTime,"A") 

A much better alternative is to give the variable a descriptive name like this one:

 intCharacterLocation=InStr(strBeginTime,"A") 

Or you can simply use this:

 intLocation=InStr(strBeginTime,"A") 

Incorrect:

  PublicFunction  StripDoubleQuotes(ByValstrString  AsString  )  AsString   '*Purpose:DoublequotescauseerrorsinSQLstatements. '*Thisprocedurestripsthemfromtheenteredtext '*andreplacesthemwithpairsofsinglequotes. '*Accepts:strStringbyvaluesothatchangesmadetoitaren't '*alsomadedirectlytothevariablebeingpassedin. '*Returns:Theoriginalstring,withalldoublequotesreplaced '*bypairsofsinglequotes.   OnErrorGoTo  PROC_ERR  Dim  intTemp  AsInteger   Dim  blnQuotesFound  AsBoolean   Const  c_DoubleQuote="""" blnQuotesFound=  False   '*Repeataslongasdoublequotesarefound.   Do   '*Lookforadoublequote.  intTemp=InStr(1,strString,c_DoubleQuote)  '*Ifadoublequoteisfound,replaceitwithapair  '*ofsinglequotes.   If  intTemp>0  Then  strString=Left$(strString,intTemp-1)&"''"&_ Mid$(strString,intTemp+1) blnQuotesFound=  True Else  blnQuotesFound=  False EndIf   LoopWhile  blnQuotesFound StripDoubleQuotes=strString PROC_EXIT:  ExitFunction  PROC_ERR:  Call  ShowError(Me.Name,"StripDoubleQuotes",Err.Number,_ Err.Description)  ResumeNext   EndFunction  

Correct:

  PublicFunction  StripDoubleQuotes(ByValstrString  AsString  )  AsString   '*Purpose:DoublequotescauseerrorsinSQLstatements. '*Thisprocedurestripsthemfromtheenteredtext '*andreplacesthemwithpairsofsinglequotes. '*Accepts:strStringbyvaluesothatchangesmadetoitaren't '*alsomadedirectlytothevariablebeingpassedin. '*Returns:Theoriginalstring,withalldoublequotesreplaced '*bypairsofsinglequotes.   OnErrorGoTo  PROC_ERR  Dim  intLocation  AsInteger   Dim  blnQuotesFound  AsBoolean Const  c_DoubleQuote="""" blnQuotesFound=  False   '*Repeataslongasdoublequotesarefound.   Do   '*Lookfordoublequotes.  intLocation=InStr(1,strString,c_DoubleQuote)  '*Ifadoublequoteisfound,replaceitwithapair '*ofsinglequotes.   If  intLocation>0  Then  strString=Left$(strString,intLocation-1)&"''"&_ Mid$(strString,intLocation+1) blnQuotesFound=  True Else  blnQuotesFound=  False EndIf   LoopWhile  blnQuotesFound StripDoubleQuotes=strString PROC_EXIT:  ExitFunction  PROC_ERR:  Call  ShowError(Me.Name,"StripDoubleQuotes",Err.Number,_ Err.Description)  ResumeNext   EndFunction  

6.3 Use mixed case in variable names.

IT'S HARDER TO READ SENTENCES IN ALL UPPERCASE THAN SENTENCES IN MIXED CASE. In the past, some languages required all uppercase letters for variable names. This limitation does not exist in Visual Basic, so use mixed case for all variable names. All lowercase is almost as bad as all uppercase. For instance, EmployeeAge is far better than EMPLOYEEAGE or employeeage . If you prefer, you can use underscore characters to simulate spaces (for example, Employee_Age ). However, if you do this, do it consistently.

Incorrect:

  Dim  strFIRSTNAME  AsString Dim  strlastname  AsString  

Correct:

  Dim  strFIRSTNAME  AsString Dim  strlastname  AsString  

Also correct:

  Dim  strFirst_Name  AsString Dim  strLast_Name  AsString  

6.4 Abbreviate only frequently used or long terms.

Although you should generally avoid abbreviating variable names, sometimes it makes sense to shorten a word or a term used for a variable name. Thirty-two characters is about the longest variable name you should create. Longer names can be difficult to read on some displays.

When you need to abbreviate, be consistent throughout your project. For instance, if you use Cnt in some areas of your project and Count in other areas, you add unnecessary complexity to the code.

One situation in which you might want to abbreviate variable names is when you're working with a set of related variables, such as these employee- related variables:

  Dim  strEmployeeName  AsString Dim  strEmployeeAddress1  AsString Dim  strEmployeeAddress2  AsString Dim  strEmployeeCity  AsString Dim  strEmployeeState  AsString Dim  strEmployeeZipcode  AsString  

Here, abbreviation becomes more attractive. If you abbreviate, be consistent. For instance, don't use variations such as EmpAddress1 , EmplAddress1 , and EmpAdd1 . Pick the abbreviation that makes sense and stick to it. Also, try to abbreviate only the part of the variable name that is the common component, not the part that is the unique specifier . In this case, abbreviating Employee is acceptable, but abbreviating Address is probably not a good idea.

Incorrect:

  Dim  strEmployeeName  AsString Dim  strEmplAdd  AsString Dim  strEmployeeCity  AsString  

Correct:

  Dim  strEmpName  AsString Dim  strEmpAddress  AsString Dim  strEmpCity  AsString  

6.5 Use qualifiers consistently.

Most projects contain variables that are related to one another. For instance, when you work with device contexts to perform drawing functions, you often have a source context and a destination context. You might also have variables that keep track of the first and last items in a set. In such situations, you should use a standard qualifier at the end of the variable name. By placing the qualifier at the end, you create more uniform variables that are easier to understand and easier to search for. For example, use strCustomerFirst and strCustomerLast instead of strFirstCustomer and strLastCustomer .

Common Variable Qualifiers

Qualifier Description
First First element in a set.
Last Last element in a set.
Next Next element in a set.
Prev Previous element in a set.
Cur Current element in a set.
Min Minimum value in a set.
Max Maximum value in a set.
Save Preserves another variable that must be reset later.
Tmp A "scratch" variable whose scope is highly localized within the code. The value of a Tmp variable is usually valid only across a set of contiguous statements within a single procedure.
Src Source. Frequently used in comparison and transfer routines.
Dst Destination. Often used in conjunction with Src .

Incorrect:

  Dim  strFirstCustomer  AsString Dim  strLastCustomer  AsString Dim  strPreviousCustomer  AsString  

Correct:

  Dim  strCustomerFirst  AsString Dim  strCustomerLast  AsString Dim  strCustomerPrevious  AsString  

6.6 Use the positive form in Boolean variables.

Boolean variable names deserve special mention. These variables have the value True or False ”nothing else. When you name Boolean variables, always use the positive form ”for instance, blnFound , blnLoaded , and blnDone rather than blnNotFound , blnNotLoaded , and blnNotDone. Using the negative form increases the chances that the variable will be handled incorrectly, particularly in complex expressions, where programming errors are more likely to occur. Positive names make Boolean variables more understandable and reduce the risk of programming errors. Notice in the incorrect example below that the negative nature of the blnNotInHere variable (used to keep track of whether or not the procedure is currently executing) means that the procedure never fully executes because a Boolean variable is initialized to False.

Incorrect:

  PrivateSub  cmdPrint_Click(Index  AsInteger  )  '*Purpose:Printorcancel,dependingonthebuttonclicked.   OnErrorGoTo  PROC_ERR  Static  blnNotInHere  AsBoolean   Const  c_Print=0  Const  c_Cancel=1  '*Preventthiseventfromexecutingtwicebecauseofauser '*double-clickingthebutton.   IfNot  (blnNotInHere)  ThenGoTo  PROC_EXIT blnNotInHere=  False   '*Determinewhichbuttonwasclicked.   SelectCase  Index  CaseIs  =c_Print  '*Printthequote.   Call  PrintQuote  CaseIs  =c_Cancel  '*Unloadtheform.  UnloadMe  CaseElse   '*Nootherindexisexpected.   EndSelect   '*Resetthevariablethatkeepstrackofwhetherwe'reinhere.  blnNotInHere=  True  PROC_EXIT:  ExitSub  PROC_ERR:  Call  ShowError(Me.Name,"cmdPrint_Click",Err.Number,_ Err.Description)  ResumeNext   EndSub  

Correct:

  PrivateSub  cmdPrint_Click(Index  AsInteger  )  '*Purpose:Printorcancel,dependingonthebuttonclicked.   OnErrorGoTo  PROC_ERR  Static  blnInHere  AsBoolean   Const  c_Print=0  Const  c_Cancel=1  '*Preventthiseventfromexecutingtwicebecauseofauser '*double-clickingthebutton.   If  blnInHere  ThenGoTo  PROC_EXIT blnInHere=  True   '*Determinewhichbuttonwasclicked.   SelectCase  Index  CaseIs  =c_Print  '*Printthequote.   Call  PrintQuote  CaseIs  =c_Cancel  '*Unloadtheform.  UnloadMe  CaseElse   '*Nootherindexisexpected.   EndSelect   '*Resetthevariablethatkeepstrackofwhetherwe'reinhere.  blnInHere=  False  PROC_EXIT:  ExitSub  PROC_ERR:  Call  ShowError(Me.Name,"cmdPrint_Click",Err.Number,_ Err.Description)  ResumeNext   EndSub  

6.7 Explicitly declare variables.

When a variable is declared with Dim , Private , Public , or Static , it's explicitly declared. Visual Basic does not require that you explicitly declare your variables, but it should. Code that includes variables that aren't explicitly declared is far more prone to errors than code with explicitly declared variables and has no place in modern software development. This is one reason why explicit declaration of variables is one of the first things a code reviewer looks for. When code simply references variables as needed (implicit declaration), it can seem as though the developer didn't give much thought to the logic of the code or the data used. Variables are important, and they deserve a formal declaration. A formal declaration at least shows that the developer gave due consideration to the use of the variable and didn't just throw it in at the last minute.

Here is a simple illustration of the problems of not explicitly declaring variables:

  PrivateSub  cmdTest_Click() strMessage="Thisisatest."  Debug.Print  strMesage  EndSub  

If you weren't paying close attention, you might have expected Visual Basic to print "This is a test." when the procedure was executed. However, Visual Basic prints an empty string. Here's what happens.

The first statement is fine. It implicitly creates a new variable called strMessage and places the string "This is a test." into the new variable.

  Line 1:  strMessage="Thisisatest." 

But here's the problem. Notice the misspelling of the variable name:

  Line 2:   Debug.Print  strMesage 

Since strMesage has not been declared as a variable and it's not a reserved word, Visual Basic automatically creates and initializes the variable "on the fly," just as it did for the variable strMessage . When variables are not explicitly declared as a specific data type, they are created as variants (which is a problem unto itself, as noted in Directive 6.9). A variant is initially empty, and therefore nothing prints. (Actually, an empty string is printed.) Note that all of this occurs without any compile-time or run-time errors.

You can easily prevent Visual Basic from allowing you to reference variables that aren't explicitly declared by adding Option Explicit to the Declarations section of a module. If Visual Basic encounters a variable that hasn't been explicitly declared in a module that contains Option Explicit in its Declarations section, an error is generated at compile time. Since you can't compile code with compile errors, you physically can't distribute these types of errors in compiled programs. Note, however, that Option Explicit applies only to the module in which it appears. Therefore, you must place it in the Declarations section of every module in your project (including Form and Class modules) for it to be fully effective.

Visual Basic can automatically add the Option Explicit statement to new modules. To enable this feature, choose Options from the Tools menu and then select Require Variable Declaration, as shown in Figure 6-1.

Note that when you enable this feature, Visual Basic does not place Option Explicit into any existing modules; you must manually add it to existing modules. The good news is that Require Variable Declaration is a systemwide setting, so all new modules created in any projects will have Option Explicit placed in their Declarations sections.

It's hard to fathom why, after all this time, Option Explicit is still an option. What's worse , it's not even the default option ”when Visual Basic is first installed, it does not enforce variable declaration; you have to turn on this feature. One of the first things you should do after installing Visual Basic is enable this feature. If you have a large project developed without Option Explicit in the Declarations sections of your modules, add Option Explicit to the modules and attempt to compile your code. You might be surprised at the results. If you find references to variables that haven't been declared, you'll probably want to put the code through some rigorous examination and testing.

click to view at full size.

Figure 6-1. Select the Require Variable Declaration check box on the Editor tab of the Options dialog box to enforce explicit variable declaration.

Incorrect:

  PrivateSub  cmdTest_Click() intValue=6  Debug.Print  intValue/2  EndSub  

Correct:

  PrivateSub  cmdTest_Click()  Dim  intValue  AsInteger  intValue=6  Debug.Print  intValue/2  EndSub  

6.8 Declare variables with carefully chosen data types.

Visual Basic provides many different data types for variables, and often, more than one data type will work in a given situation. Choosing the best data type for each variable can reduce memory requirements, speed up execution, and reduce the chance of errors. Also, different data types have different resource requirements, and the data type you use for a variable can affect the results of computations based on that variable. For instance, consider the following code:

  PrivateSub  cmdCalculate_Click()  Dim  sngFirst  AsSingle Dim  sngSecond  AsSingle Dim  intResult  AsInteger  sngFirst=3 sngSecond=0.5 intResult=sngFirst+sngSecond  Debug.Print  intResult  EndSub  

When this code is executed, the number 4 is printed in the Immediate window. The correct result should, of course, be 3.5. However, Visual Basic does its best to follow your instructions and rounds the result of the calculation to fit in the Integer variable. Visual Basic doesn't raise a run-time error in situations like this ”it simply forces the value to fit the data type. Such problems can be extremely tricky to debug. In this example, simply changing the type of the intResult variable to Single solves the problem.

Coercing (rounding) is not the only problem created by incorrect numeric data types. When you perform numeric calculations, you must be mindful of possible overflow errors. If you attempt to put a value into a variable and the value is greater than the maximum value for the data type, the run-time error "6 ” Overflow" occurs. (See Figure 6-2.)

Figure 6-2. When you attempt to put too large a value in a variable, this overflow error occurs.

This type of error is easy to demonstrate . Consider the following code:

  PrivateSub  cmdCalculate_Click()  Dim  intFirst  AsInteger Dim  intSecond  AsInteger Dim  intResult  AsInteger  intFirst=32000 intSecond=32000 intResult=intFirst+intSecond  Debug.Print  intResult  EndSub  

The maximum value that can be stored in an Integer variable is 32,767. Since intFirst and intSecond are both assigned the value 32,000, no error is generated. However, when you attempt to place the result of adding these two numbers (64,000) in intResult (another Integer variable), an overflow error occurs. In this case, changing intResult to a Long solves the problem. Fortunately, overflow errors are easier to debug than rounding problems, but they often aren't apparent until the application is used live.

When you create a variable, consider whether the variable will hold the results of calculations based on other variables. In some cases, going to the next larger data type (Double from Single, Long from Integer) is the safest bet. In others, you might find that there is an even more specific data type available, such as the Currency data type for holding monetary values.

Incorrect:

  PrivateSub  PrintTax(sngTaxableAmount  AsSingle  ,lngTaxRate  AsLong  )  '*Purpose:Printthetotaltax(taxableamount*taxrate). '*Accepts:sngTaxableAmount-thedollarvaluetowhichtax '*applies. '*lngTaxRate-thelocaltaxrate.   OnErrorGoTo  PROC_ERR  Dim  sngTax  AsSingle  sngTax=sngTaxableAmount*lngTaxRate  Debug.Print  sngTax PROC_EXIT:  ExitSub  PROC_ERR:  Call  ShowError(Me.Name,"PrintTax",Err.Number,Err.Description)  GoTo  Proc_Exit  EndSub  

Correct:

  PrivateSub  PrintTax(curTaxableAmount  AsCurrency  ,_  sngTaxRate  AsSingle  )  '*Purpose:Printthetotaltax(taxableamount*taxrate). '*Accepts:curTaxableAmount-thedollarvaluetowhichtax '*applies. '*sngTaxRate-thelocaltaxrate.   OnErrorGoTo  Proc_Err  Dim  curTax  AsCurrency  curTax=curTaxableAmount*sngTaxRate  Debug.Print  curTax PROC_EXIT:  ExitSub  PROC_ERR:  Call  ShowError(Me.Name,"PrintTax",Err.Number,Err.Description)  GoTo  Proc_Exit  EndSub  

The following table lists the Visual Basic data types.

Visual Basic Data Types

Data Type Value Range
Byte 0 to 255
Boolean True or False
Integer -32,768 to 32,767
Long -2,147,483,648 to 2,147,483,647
Single -3.402823E38 to -1.401298E-45 for negative values
1.401298E-45 to 3.402823E38 for positive values
Double -1.79769313486232E308 to -4.94065645841247E-324 for negative values
4.94065645841247E-324 to 1.79769313486232E308 for positive values
Currency -922,337,203,685,477.5808 to 922,337,203,685,477.5807
Date January 1, 100 to December 31, 9999
Object Any object reference
Variable-length String 0 to approximately 2 billion characters
Variant Numbers: any numeric value up to the range of a Double
String: same range as for variable-length String

Here are some helpful guidelines for using data types:

  • To store any type of text, use the String data type. This data type can contain any valid keyboard character, including numbers and nonalphabetic characters.
  • To store only the values True and False, use the Boolean data type.
  • To store a number that contains no decimal places, is greater than or equal to -32,768, and is less than or equal to 32,767, use the Integer data type.
  • To store numbers with no decimal places but with values greater than or less than those that an Integer will allow, use the Long data type. Longs are often referred to as Long Integers, but they're defined using the keyword Long.
  • To store numbers that contain decimal places, use the Single data type. Unless you're writing incredibly complex mathematical applications that require extremely large numbers or numbers very close to 0, this data type should work for almost all your values containing decimals. If you need to store larger numbers than the Single data type can hold, use the Double data type.
  • To store dollar amounts, use the Currency data type. This data type uses a fixed decimal point of four places. However, unless you specify a value with four decimal places, only two are shown when you display the value.
  • To store a date or a time value, use the Date data type. When you use this data type, Visual Basic recognizes common date and time formats. For example, if you store the value 7/22/1997, Visual Basic does not treat it as a simple text string; it knows that it represents July 22, 1997.

6.9 Use the Variant data type only when absolutely necessary.

Visual Basic is extremely lenient on the typing of variables. It's quite content to "protect" you by coercing data when it feels it's appropriate. Nowhere is this more evident than in the use of the Variant data type. This data type is often abused, and there is almost always a better alternative to the Variant data type. When used correctly, a Variant is a great tool for certain tasks . You can create Variant variables that can hold any type of data, including objects. Visual Basic is even fairly smart at determining the type of data a Variant holds and applying the appropriate behavior to the determined type. For instance, take a look at the following code:

  Dim  vntA  AsVariant Dim  vntB  AsVariant  vntA="1" vntB=2  Debug.Print  vntA+vntB 

When executed, the code prints 3 , even though the variable vntA contains a string. When Visual Basic encounters the + operator, it determines that, if at all possible, it must perform arithmetic using numbers. Visual Basic evaluates the two Variant variables in the preceding code and determines that they do indeed each contain a number. It then adds the two numbers and gets a result of 3. However, by changing a single character in the procedure, as shown below, the outcome is completely different:

  Dim  vntA  AsVariant Dim  vntB  AsVariant  vntA="1" vntB=2  Debug.Print  vntA&vntB 

When this code is executed, 12 is printed. The ampersand (&) character is used for string concatenation; when Visual Basic encounters the ampersand, it evaluates the two variables to determine whether their values can be interpreted as Strings. Although only one of the variables ( vntA ) contains a String, Visual Basic decides that it can still represent the other variable's value as a String. This is called coercion or (somewhat less affectionately) evil type coercion (ETC). The result of such coercion is often unpredictable. Visual Basic can sometimes coerce strictly typed variables (variables declared as specific data types), but it is more likely to do this with Variants. The previous two code samples produce the same results even if the variables are declared as specific types such as String and Long. However, compare the following two code examples:

  Dim  strA  AsString Dim  strB  AsString  strA=1 strB=2  Debug.Print  strA+strB 

and

  Dim  vntA  AsVariant Dim  vntB  AsVariant  vntA=1 vntB=2  Debug.Print  vntA+vntB 

The first code example prints 12. When Visual Basic works with String variables, it treats the + symbol as a concatenation operator. However, recall that the default behavior of the + symbol is for arithmetic. In the second example, Visual Basic looks at the Variant variables to see if their values can be treated as numbers, which in this case they can. Therefore, it adds them and prints 3. Other than the data types (and the names of the variables to enforce the naming prefixes), these two procedures are identical ”but they produce different values.

NOTE
Never use the + symbol for string concatenation ”use an ampersand.

The simple fact that Visual Basic treats data in a Variant variable differently at different times should be enough to dissuade you from using Variants when they aren't necessary. Variants have other pitfalls as well. For instance, they consume more memory than any other Visual Basic data type ”16 bytes for any numeric value up to the range of a Double and a whopping 22 bytes plus the string length when they hold string data! Arrays of any data type require 20 bytes of memory plus 4 bytes for each dimension ”for example, Dim MyArray(9, 5) declares an array with 2 dimensions ”plus the bytes for each data element itself. Consequently, if you create an array of Variants, you should have a darned good reason for doing so. Not only do Variants consume more memory than other data types, but manipulating the data within them is almost always slower than performing the same manipulation with strict data types.

Declaring a variable with As Variant isn't the only way to create a Variant variable. If you neglect to include an As <type> clause in a variable declaration, the variable is created as a Variant. For instance, the following two declarations yield the same result:

  Dim  MyVariable  Dim  MyVariable  AsVariant  

The same holds true for Function procedures. If you neglect to include an As <type> clause when you define a function, the return value of the function is always a Variant. The following definitions yield the same result:

  PrivateFunction  MyFunction()  PrivateFunction  MyFunction()  AsVariant  

For the reasons stated above, you should always declare variables and Function procedures as specific types. If you want to declare either one as type Variant, explicitly declare the variable or Function procedure by using As Variant .

In addition to omitting an As <type> clause when declaring a variable or Function procedure, there's another all-too-common way to inadvertently create Variant variables. Consider this line of code:

  Dim  VariableA,VariableB,VariableC  AsString  

What are the data types of these three variables? If you said that they're all Strings, you're probably not alone, but you're definitely not correct. Visual Basic allows you to declare multiple variables on a single line to save space ”not to make it easy to declare multiple variables of the same data type (which would be the obvious reason). When you declare multiple variables on the same line with a single As <type> clause, only the last variable in the Dim statement is given the specified type. All the other variables are allocated as Variants. For this reason, you should always declare each individual variable on a separate line. If you must declare multiple variables on a single line ”and you should avoid this ”declare each variable with an As <type> clause, like this:

  Dim  VariableA  AsString  ,VariableB  AsString  

By paying close attention to your declaration of variables, you can keep the number of Variant variables to a minimum, resulting in leaner, quicker, and often more dependable code.

Incorrect:

  Dim  vntControl  AsVariant   '*Loopthroughallcontrolsontheformandprinttheirheights.   ForEach  vntControl  In  Me.Controls  Debug.Print  vntControl.Height  Next  vntControl 

Correct:

  Dim  cntControl  AsControl   '*Loopthroughallcontrolsontheformandprinttheirheights.   ForEach  cntControl  In  Me.Controls  Debug.Print  cntControl.Height  Next  cntControl 

Practical Application

6.9.1 Declare only one variable per line if possible. To reduce the possibility of inadvertently creating Variant variables and to create more readable code, refrain from declaring multiple variables on the same line.

Incorrect:

  Dim  strName,strAddress  AsString  

Also incorrect:

  Dim  intAge  AsInteger  ,strName  AsString  ,strAddress  AsString  

Correct:

  Dim  intAge  AsInteger Dim  strName  AsString Dim  strAddress  AsString  

6.10 Minimize variable scope.

The scope of a variable is its visibility to other procedures and modules. Visual Basic essentially offers three levels of scope: procedural, module, and global. These levels are explained in the following table.

Levels of Scope in Visual Basic

Scope Description
Procedural The variable is declared within the procedure and therefore can be seen and used only within the procedure. Variables with procedural scope are often called local variables.
Module The variable is declared (with Dim or Private ) in the Declarations section of a module and is visible to all procedures within the module.
Global The variable is declared as Public within a standard module. The variable is visible to all procedures within all modules of the project.

You should try to minimize scope as much as possible. The smaller the scope of a variable, the lower the odds of errors related to the variable. Also, when variables go out of scope, their resources are reclaimed by the operating system. If the scope of a variable is wider than it needs to be, the variable might remain in existence and consume resources long after it's no longer needed. If data needs to be shared between procedures, pass the data as parameters between the procedures if at all possible.

Not all global variables are evil, but if you think of them as being generally demonic, you'll write better code. Unfortunately, some programmers treat the global variable as a panacea; they declare it once and use it for ever. Some even (gasp!) use a single global variable for multiple purposes!

The problems inherent with global variables are numerous . Their major problem is that they can be changed by any procedure within any module, and it's difficult to track where these changes take place. For instance, suppose that Procedure A makes use of a global variable. Procedure A reads the value of the variable and then calls Procedure B. Procedure B in turn calls Procedure C, which changes the value of the global variable. Procedure C ends and returns execution to Procedure B, which then ends and returns execution to Procedure A. Procedure A, meanwhile, is totally unaware of the change made to the global variable by Procedure C and proceeds to perform actions based on the original value of the global variable. Debugging in such scenarios is a nightmare.

As I mentioned earlier, consumption of resources is an important aspect of scope. With a single, ordinary variable, resource consumption is minimal. Imagine, however, the problems associated with creating global-level Recordset variables! Record locking and resource concerns are just a few of the issues associated with global-level Recordsets. Consider a procedure operating on the assumption that the Recordset is pointing to one record when it's actually pointing to another. With Recordset variables, minimizing scope can have a huge impact on the reliability of the application.

When you find yourself declaring a global variable, stop and look for a way to obtain the same functionality without using a global variable. You can usually find a better option, but it's not always obvious. For instance, if a module-level variable is used in only one procedure, you should make it a static variable local to that procedure. By doing so, you eliminate the possibility of another procedure inadvertently modifying the contents of the variable.

An excellent example of the use of a static variable can be found in the prevention of reentrancy. When we released our first Microsoft Windows program to our users, they experienced all sorts of errors. As developers, we took for granted that people knew to click buttons and toolbars only once. We came to find out that many users believed that double-clicking was the proper way to trigger all actions. Consequently, the Click event of a given button would execute on the second click of a double-click, but the event would still be in the process of executing from the first click. This is known as reentrancy, and it causes very strange and undesirable behavior. Users were also causing reentrancy when they double-clicked an item in a list or grid to perform an action. If the computer took slightly longer to perform the function than the user expected, they'd double-click the item again, causing the double-click to fire a second time while still executing code from the first time. Using a static variable, as shown in the following procedure, you can prevent reentrancy from causing problems in a procedure. Note, if you use a global variable instead of a static one, you run the risk of inadvertently changing the variable's value in another procedure that's performing the same technique.

  PrivateSub  grdListFind_DblClick()  '*Purpose:Displaytheselectedrecordwhentheuser '*double-clicksarowonthegrid.   OnErrorGoTo  PROC_ERR  Static  blnInHere  AsBoolean   '*Protectthiseventfromreentrancycausedbyanoverzealoususer.   If  blnInHere  ThenGoTo  PROC_EXIT  '*Flagthatwe'reinhere.  blnInHere=  True   Call  ShowSelectedRecord blnInHere=  False  PROC_EXIT:  ExitSub  PROC_ERR:  Call  ShowError(Me.Name,"grdListFind_dblClick",Err.Number,_   Err.Description)  ResumeNext EndSub  

When you define variables, remember to use a scope prefix to clearly indicate the scope of the variable. The table on the following page lists the possible prefixes. (For more information on naming conventions and Hungarian prefixes, see Chapter 4.)

Scope Prefixes for Variables

Prefix Description Example
g Global g_strSavePath
m Local to module or form m_blnDataChanged
st Static st_blnInHere
(no prefix) Nonstatic variable; variable is local to the procedure  

6.11 Concatenate strings by using an ampersand.

A common mistake that new Visual Basic programmers make is to use the plus sign (+) to concatenate Strings. The plus sign is used in arithmetic to add two numbers, so to many people it feels natural to use the plus sign to concatenate two Strings. Visual Basic even lets you use the plus sign for concatenation, but you should never do this; the ampersand is the official symbol for concatenating Strings. The main problem with using the plus sign arises when one of the variables that you're concatenating is not a String variable. If a variable has a numeric data type, Visual Basic performs arithmetic rather than concatenation. Consider the following code:

  Dim  strFirst  AsString Dim  strSecond  AsString  strFirst="1" strSecond="2"  Debug.Print  strFirst+strSecond 

When this code is executed, 12 is printed in the Immediate window. Compare the previous code to the following:

  Dim  strFirst  AsString Dim  sngSecond  AsSingle  strFirst="1" sngSecond="2"  Debug.Print  strFirst+sngSecond 

The only difference is that the data type of sngSecond is now a Single. When this code is executed, the number 3 is printed. Because of this possible coercion, the only acceptable method of concatenation is to use the ampersand. The ampersand clearly conveys the intent of the function, removing any doubt a reader might have, and eliminates the possibility of incorrect results because of type coercion.

Incorrect:

  Dim  strFirstName  AsString Dim  strLastName  AsString Dim  strFullName  AsString  strFirstName="Ethan" strLastName="Foxall"  '*Concatenatethefirstandlastnamesintoacompletename.  strFullName=strFirstName+strLastName 

Correct:

  Dim  strFirstName  AsString Dim  strLastName  AsString Dim  strFullName  AsString  strFirstName="Ethan" strLastName="Foxall"  '*Concatenatethefirstandlastnamesintoacompletename.  strFullName=strFirstName&strLastName 


Practical Standards for Microsoft Visual Basic
Practical Standards for Microsoft Visual Basic .NET (Pro-Developer)
ISBN: 0735613567
EAN: 2147483647
Year: 2000
Pages: 57

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net