ISBN 0-7356-0733-8
Chapter 5 and the Table of Contents of Practical Standards for Microsoft Visual Basic is reprinted with permission from Microsoft Press.
http://www.microsoft.com/mspress/
Acknowledgments
PART 1 Design
Chapter 1 Introduction to Standards
Why This Book Is Necessary
About This Book
Chapter 2 Creating Object and Project Templates
Using Object Templates
Using Project Templates
Exploring Visual Basic Project Templates
Creating Custom Project Templates
Customizing Template Behavior
Enabling and Disabling Templates
Setting the Templates Folder
Directives
2.1 Never hard-code application-specific or component-specific values in object templates.
2.2 Provide extensive comments in object templates, particularly where modifications are required.
Chapter 3 Designing Modules and Procedures
Create Modules That Have Strong Cohesion
Create Loosely Coupled, Highly Specialized Procedures
Make All Procedures Perform Specialized Functions
Make Procedures as Self-Contained as Possible
Minimize Fan-In and Fan-Out
Attempt to Alphabetize Procedures Within a Module
Directives
3.1 Give procedures and modules descriptive names.
3.2 Give every procedure a single exit point.
3.3 Give every procedure a clearly defined scope.
3.4 Use parameters to pass data between procedures.
3.5 Call procedures in a consistent and self-documenting manner.
PART 2 Conventions
Chapter 4 Naming Conventions
Data Type Suffixes
Hungarian Notation
Denoting a Variable's Data Type
Denoting a Variable's Scope
Other Prefixes
Chapter 5 Using Constants and Enumerations
Using Constants
Magic Numbers Are Prone to Data Entry Problems
Magic Numbers Are Difficult to Update
Constants Make Code Easier to Read
Using Enumerations
Creating Custom Enumerations
Using a Custom Enumeration
Directives
5.1 Prefix all constants with c_ and a scope designator.
5.2 Use constants in place of magic numbers, regardless of scope.
5.3 Use enumerations whenever they are available.
5.4 Use constants when you refer to elements of a control array.
5.5 Use an application or company prefix for enumeration members.
5.6 Use system constants when enumerations aren't available.
5.7 Use an enumeration whenever a parameter accepts a limited number of values.
Chapter 6 Variables
Directives
6.1 Define focused variables.
6.2 Give variables descriptive names.
6.3 Use mixed case in variable names.
6.4 Abbreviate only frequently used or long terms.
6.5 Use qualifiers consistently.
6.6 Use the positive form in Boolean variables.
6.7 Explicitly declare variables.
6.8 Declare variables with carefully chosen data types.
6.9 Use the Variant data type only when absolutely necessary.
6.10 Minimize variable scope.
6.11 Concatenate strings by using an ampersand.
Chapter 7 Error Handling
Visual Basic's Compilation Options
The Err Object
Types of Error Handlers
Ignoring Errors by Using On Error Resume Next
Diverting the Flow of Execution by Using On Error GoTo
Error Handlers and the Call Stack
Disabling Error Handlers at Run Time by Using On Error GoTo 0
Enabling and Disabling Error Handlers in Debug Mode
Central Error Handlers
Logging Errors to a Text File
Directives
7.1 Use On Error GoTo to trap unexpected errors.
7.2 Use On Error Resume Next to trap expected errors.
7.3 Create consistent error handler blocks.
PART 3 Coding Constructs
Chapter 8 Formatting Code
Directives
8.1 Do not place multiple statements on a single line.
8.2 Use the line continuation character.
8.3 Indent continuation lines.
8.4 Use indentation to show organizational structure.
8.5 Indent code within the Declarations section of a module to show subordination.
8.6 Use white space to group related statements.
Chapter 9 Commenting Code
Directives
9.1 Document the purpose of the code.
9.2 If you need to violate good programming style, explain why.
9.3 Document when an error is expected and why.
9.4 Comment before writing code.
9.5 Use solid-character comment lines only for major comments.
9.6 Avoid creating comment boxes.
9.7 Use an apostrophe to denote comments.
9.8 Make your comments readable.
9.9 Indent comments to align them with the statements that follow.
9.10 Give each procedure a comment header.
9.11 Document code processes by using inline comments.
9.12 Use end-of-line comments to document variables.
Chapter 10 Looping Structures
Directives
10.1 Use For.Next to loop a specific number of times.
10.2 Use Do.Loop to loop an undetermined number of times.
10.3 Use Do.Loop in place of While.Wend.
10.4 Use For Each.Next to loop through all members of a collection.
Chapter 11 Controlling Code Flow
Directives
11.1 Use If.Then.Else when the decision is based on one condition being True or False.
11.2 Use Select Case when comparing a non-Boolean expression to a variety of possible values.
11.3 Use end-of-line comments to add clarity to nested decision structures.
11.4 Format expressions for accurate evaluation and ease of understanding.
11.5 Refrain from using GoSub whenever possible.
11.6 Use GoTo only when there are no other alternatives or when jumping to an error handler or single exit point.
PART 4 User Interaction
Chapter 12 Interface Design
The Necessity of Consistent Interface Design
Directives
12.1 Give forms a consistent appearance and behavior.
12.2 Present controls with a standard appearance.
12.3 Use the best interface component for a given situation.
12.4 Provide comprehensive and sensible menus.
12.5 Use system colors wherever possible.
Chapter 13 User Input and Notification
User Input
Notifications
Directives
13.1 Ensure thorough keyboard navigation and interaction.
13.2 Provide consistent and intuitive mouse interaction.
13.3 Create thoughtful and functional message boxes.
PART 5 Team Projects
Chapter 14 Version Control
Directives
14.1 Increment the version number each time you compile a program.
14.2 Display a program's version number in the About dialog box.
14.3 Maintain backward compatibility in ActiveX components.
14.4 Document changes in a Readme file.
14.5 Back up your files.
14.6 Use Microsoft Visual SourceSafe to maintain versions of source code.
Chapter 15 Source Code Control
Identifying the Challenges of Team Development
Understanding Visual SourceSafe
Setting Up Visual SourceSafe
Creating a Visual SourceSafe Database
Opening a Visual SourceSafe Database
Adding Users to a Visual SourceSafe Database
Placing a Visual Basic Project Under SourceSafe Control
Visual Basic Projects and Visual SourceSafe
Designating a Working Folder
Creating a Working Copy of the Project
Checking Out Files by Using Visual SourceSafe Explorer
Checking Files In and Out from the Visual Basic IDE
Adding New Files to a Project Under Source Code Control
Getting the Latest Version of Files
Comparing Revisions
Index
When you hard-code numbers in your procedures, a myriad of things can go wrong. Hard-coded numbers are generally referred to as "magic numbers" because they're often shrouded in mystery; the meaning of such a number is obscure because the digits themselves give no indication as to what the number represents. This chapter discusses the drawbacks of magic numbers and offers alternatives using constants and enumerations.
A constant is much like a variable; you create a name and assign it a value. However, unlike a variable, a constant is given its value at design time, and this value cannot be changed at run time. You should always use constants in place of magic numbers, for reasons I'll discuss in this section.
Note: When you hard-code a string in a procedure, the effect is similar to that of using magic numbers. All the reasons for eliminating magic numbers also apply to eliminating hard-coded strings; if you know the value at design time, use a constant rather than hard-coding the text or the number.
Magic Numbers Are Prone to Data Entry Problems
One of the critical problems with magic numbers is that you can easily mistype a number, transposing its digits. When you type the number 10876, for instance, it's not at all difficult to mistakenly type 10867 or 18076. In contrast to the way it handles variables and reserved words, Microsoft Visual Basic's compiler takes no issue with transposed or incorrect numbers—it's happy to use whatever magic number you supply it. Sometimes the problems caused by a simple mistake don't surface immediately, and when they do they can appear as random miscalculations that are difficult to pinpoint. When you use a constant in place of a magic number, Visual Basic checks the validity of the constant at compile time. If the constant does not exist, Visual Basic tells you so and refuses to compile. This eliminates the problem of inaccurately typed numbers; as long as the single constant has the correct value, all the code that uses that constant will use the correct value as well.
Note: If you don't include the Option Explicit statement within the Declarations section of a module and you mistype a constant name, Visual Basic implicitly declares a variable with the incorrect name of the constant, causing inaccurate results. This is just one more reason to explicitly declare your variables. See Chapter 6, "Variables," for more information on explicit vs. implicit variable declaration.
Magic Numbers Are Difficult to Update
Another serious drawback to magic numbers is that they're difficult to keep updated. Say you're developing a financial application and the current mortgage interest rate is 7.25 percent. Also assume that this value is hard-coded in a number of procedures that perform calculations based on the interest rate. What do you do when the rates change (which they do regularly)? You could perform a global search and replace, but that exposes your code to errors. Another loan rate used within the application might also have the value 7.25. If you perform a global search and replace on 7.25 percent, you'll change that loan rate as well. If you manually change each value in the code, you risk transposing or otherwise mistyping the new values. A selective search and replace with a confirmation on each change would be time-consuming. If you use a constant instead, you simply change the value once (in the constant's declaration), and every line of code that uses the mortgage interest rate instantly uses the new, updated rate.
Constants Make Code Easier to Read
A by-product of using constants to create more error-free code is code that is much more readable. Generally, magic numbers are anything but intuitive. They might be obvious to you, but they can be difficult for others to decipher. By intelligently naming your constants, the code that uses those constants becomes a bit more self-documenting and a lot easier to read. Consider the following two code statements. Which makes the most sense to you?
Magic numbers:
curInterestAmount = (curLoanAmount * .06) / 12
Named constants:
curInterestAmount = _ (curLoanAmount * c_sngInterestRate) / c_intMonthsInTerm
One last note on constants: it's quite acceptable to give constants higher scope, unlike when you use variables, a situation in which it's highly advisable to reduce scope. As a matter of fact, you should never create the same constant twice within an application. If you find that you're duplicating a constant, move the original declaration to a higher scope until the constant becomes available to all procedures that reference it.
Enumerations are similar to constants in that they are named entities that are assigned values. However, enumerations behave like groups of public constants in a module. They're treated as data types, and you use them to create constants of suitable values for variables and properties. You might already be using enumerations in Visual Basic. For instance, when you use the MsgBox statement, Visual Basic's Auto List Members feature displays a drop-down list (commonly referred to as the "code helper drop-down list") for the Buttons parameter. (See Figure 5-1.)

Figure 5-1: Enumerations eliminate the need to memorize many different parameter values.
See full-sized image.
The time that the developers of the MsgBox statement invested to provide the Buttons parameter's values in an enumeration pays you dividends as a programmer. You never have to remember the numeric values of the parameter, and the chance of incorrectly specifying a value is greatly diminished. Although you can still specify a numeric value for a parameter rather than the name of an enumeration member, you should never do so. To actually use a magic number when an associated member name is available is just south of insane.
Creating Custom Enumerations
You create enumerations much like you do user-defined data types. In the Declarations section of a module, you type the word Public or Private, type Enum, and then type the name of your custom enumeration. The following is a sample enumeration:
Public Enum otBorderStyle otNone = 0 otRaised_Light = 1 otRaised_Heavy = 2 otSunken_Light = 3 otSunken_Heavy = 4 End Enum
This enumeration creates an enumerated type with five values. Although you always refer to the name of an enumeration member when you write code, the name simply represents its numeric value, much like a constant. All members of enumerations are long integers; you can't use other data types.
Using a Custom Enumeration
Once you've created an enumeration, you can use it as the data type for any variable, Function procedure, or Property procedure within the scope of the enumeration. For instance, to create a BorderStyle property that uses the enumerated type shown previously, you can declare a procedure like this:
Public Property Let BorderStyle(lngNew_BorderStyle _ As otBorderStyle) End Property
This property procedure accepts a value into the parameter lngNew_BorderStyle. lngNew_BorderStyle is a long integer because it is declared as an enumerated type and all members of enumerations are long integers. When you reference this property in code, Visual Basic displays the code helper drop-down list with all the enumeration members, as shown in Figure 5-2.

Figure 5-2: Custom enumerations offer the same advantages and behavior as system enumerations.
See full-sized image.
You can also use an enumerated type as the data type returned by a function or a Property procedure. For instance, to create the Property Get procedure that corresponds to the previous Property Let procedure, you can use code like this:
Public Property Get BorderStyle() As otBorderStyle End Property
Note that simply declaring a parameter as an enumerated type does not guarantee that the value passed to the parameter will be one of the defined enumeration members. As a matter of fact, this is one of the most common misconceptions about enumerated types. When you define an enumeration, you define a list of named values, but these are not the only values that a parameter of that enumerated type will accept. As mentioned previously, parameters that are defined as enumerated types are actually long integers. As such, they accept any value that fits into a long integer; Visual Basic does not confirm that the number passed into a parameter corresponds to a member of the enumeration. For this reason, you should always validate the data passed to a parameter declared as an enumerated type, as I'll discuss in Directive 5.8.
Goals of Using Constants and Enumerations
The goals of using constants and enumerations include
| • | Reducing errors caused by transposing or mistyping numbers |
| • | Making it easy to change values in the future |
| • | Making code easier to read |
| • | Ensuring forward compatibility |
5.1 Prefix all constants with c_ and a scope designator.
In the past, one convention for denoting a constant was to use all uppercase letters for the constant's name. For instance, when you created a constant to store a column index in a grid, you would use a statement like this:
Const COLUMN_INDEX = 7
Typing anything in code in all uppercase letters is now considered antiquated and undesirable. Mixed-case text is much easier to read. However, since variable and procedure names are also entered in mixed case, it's important to denote when an item is a constant. A better convention is to prefix the constant name with c_. For example, the constant shown above would be declared like this:
Const c_Column_Index = 7
This constant name is a bit easier to read, and you can still immediately tell that you're looking at a constant as opposed to a variable. The second underscore is optional. Some developers (including me) prefer not to use an underscore in this way. This is fine, as long as your approach is consistent. The same constant declaration without the second underscore would look like the following line of code. (Remember that you'll always have an underscore in the constant prefix.)
Const c_ColumnIndex = 7
Note: Labels for use with GoTo are one of the few exceptions to using mixed-case letters. Such labels, which should be used sparingly, appear in all uppercase letters. Refer to Chapter 11, "Controlling Code Flow," for more information on using these labels.
Another identifying characteristic of a constant as opposed to a variable is the lack of a data type prefix. For instance, if you were storing the column indicator in a variable, you would probably declare the variable by using a statement like this:
Dim intColumnIndex As Integer
Note: Some external libraries still use uppercase constants. For instance, if you use the API viewer to locate and copy API-related constants, you'll often see these constants in uppercase letters. In such cases, leave the constants as they are to promote cross-application consistency.
Many developers don't realize that you can actually create a constant of a specific data type. For instance, the following statement is completely legal:
Const c_InterestRate As Single = 7.5
You can specify a data type for a constant, but it adds complexity, and I don't know of a good reason to do so. If you decide to do it anyway, you should use the variable-naming prefixes discussed in Chapter 4, "Naming Conventions." The previous declaration, for instance, is not correct—according to the directives presented in this book—because the data type prefix is omitted. The proper declaration would be as follows:
Const c_sngInterestRate As Single = 7.5
Although the prefix for constants is different from the prefixes for variables, you should still use the same prefix scheme for indicating the scope of constants that you use for variables. For constants declared locally (within a procedure), no scope indicator is necessary. For constants declared as Private in the Declarations section of a module, you should use the prefix m. For global constants (constants declared as Public within a standard module), you should use the prefix g. The following are declarations of the same constant at different levels of scope:
Procedure: Const c_InterestRate = 7.5 Module (private): Private Const mc_InterestRate = 7.5 Global: Public Const gc_InterestRate = 7.5
Note: Constants are declared Private by default if you don't explicitly declare them with the Public keyword. As with procedures and variables, constants should always have a clearly defined scope. If you want to create a private constant, explicitly declare the constant using the Private keyword.
By consistently specifying the scope of a constant in addition to denoting the constant with c_, you'll make your code easier to read and to debug. If you're ever unsure where a constant is declared, simply place the cursor anywhere within the name of the constant and press Shift+F2. Visual Basic will take you directly to the constant's declaration.
Practical Applications
When you uniquely identify constants and denote their scope, you create more readable code.
5.1.1 Declare constants using mixed-case characters, prefixing each constant with c_. Remember that identifying constants by using all uppercase letters is out.
Incorrect:
Const USDATE = "mm/dd/yyyy" Const KEYCONTROL = 17
Correct:
Const c_USDate = "mm/dd/yyyy" Const c_KeyControl = 17
Also correct:
Const c_US_Date = "mm/dd/yyyy" Const c_Key_Control = 17
5.1.2 Denote a constant's scope using a scope designator prefix. Knowing a constant's scope is extremely important for debugging. All constants declared in the Declarations section of any type of module need a g or an m designator.
Incorrect (module level or global level):
Private Const c_US_DATE = "mm/dd/yyyy" Public Const c_KeyControl = 17
Correct:
Private Const mc_US_Date = "mm/dd/yyyy" Public Const gc_KeyControl = 17
5.2 Use constants in place of magic numbers, regardless of scope.
I hope that the first part of this chapter has convinced you of the importance of replacing hard-coded numbers (magic numbers) with constants, regardless of scope. It might be tempting to use a hard-coded number within a procedure because it seems silly to create a constant for a single use in a single place. Certainly, maintaining the value is easy enough; you don't need to perform a search and replace when the number exists only once. However, readability is still a problem. Magic numbers are called "magic" for a reason. When someone else looks at the code, how can you be sure that the number will make complete sense to him or her? Regardless of scope, you should always replace magic numbers with constants. All together now: "Use constants in place of magic numbers, regardless of scope."
Incorrect:
'* Fill a most recently used list. For intCount = 1 To 4 strFileName = RetrieveRecentFileName(intCount) '* If an entry was found, add it to the list. If strFileName <> "" Then Set objItem = lvwRecent.ListItems.Add() objItem.Text = strFileName objItem.SmallIcon = "Database" End If Next intCount
Correct:
'* Fill a most recently used list. Const c_Max_Recently_Used = 4 For intCount = 1 To c_Max_Recently_Used strFileName = RetrieveRecentFileName(intCount) '* If an entry was found, add it to the list. If strFileName <> "" Then Set objItem = lvwRecent.ListItems.Add() objItem.Text = strFileName objItem.SmallIcon = "Database" End If Next intCount
5.3 Use enumerations whenever they are available.
When a procedure or a parameter is declared as an enumerated type, it's common courtesy—nay, it's your duty—to reference the enumeration member names rather than their values. When you use an enumeration, your code is much easier to read and less likely to contain errors. Your code is also less likely to fail if someone later breaks backward compatibility by changing the values that correspond to the member names. For instance, say you're using an ActiveX component and one of its properties is BorderStyle. The developer has designated 0 - Flat and 1 - 3D as the possible values and has exposed them as members of an enumeration, as shown here:
Public Enum BorderStyle psFlat = 0 ps3D = 1 End Enum
Say that you use the literal values rather than the enumeration's member names and you're updating to a new component. The developer has added a new BorderStyle value called Chiseled to the component. However, he wasn't really thinking of backward compatibility, and he changed the enumeration structure to the following:
Public Enum BorderStyle psFlat = 0 psChiseled = 1 ps3D = 2 End Enum
You can see that if you hard-code 1 to designate a 3-D border, you'll get unexpected results after you upgrade. Obviously, component developers should not break backward compatibility in this way, but it does happen. If you use the member's name rather than its value, your code will not be affected by such an oversight. Whether to use enumerations shouldn't even be a question. If a function supports them, use them. In new versions of software components, enumerations are often provided where there were none before. As enumerations and constants become available, make sure to change your code appropriately to use them.
Incorrect:
MsgBox "Print all documents?", 36
Correct:
MsgBox "Print all documents?", vbYesNo Or vbQuestion
5.4 Use constants when you refer to elements of a control array.
One area in which constants really shine but are often underused is as references to indexes of control arrays. When you create a control array, you end up with a number of controls all with the same name. To reference a particular control, you use its shared name and its unique index. When you hard-code an index, you create a magic number—complete with all the drawbacks discussed earlier. The problem with hard-coding index references is that your code can be difficult to debug if you've referenced the wrong index. Since controls within a control array are always of the same type, you can switch indexes all day long with little chance of generating an error message because all the actions you might perform on one member can be performed on another member.
To diminish the possibility of errors when you use control arrays, you should create constants that relate to each index. For example, say you have three text boxes that store a home phone number, work phone number, and mobile phone number, respectively. You use a control array because you have some standard code that runs on the Validate event to verify that each number is indeed a valid phone number. If you hard-code indexes, you have to remember which index references which type of phone number. If you're in a hurry or you haven't had your morning Mountain Dew, you can easily confuse the numbers. However, if you assign a constant to each index and always reference a control by its constant, never by its index directly, it's easier to ensure accuracy. Control arrays can make certain development situations much easier, and they can make an application less resource-intensive. However, the more elements you create for a control array, the more likely it is that an incorrect element will be referenced in code. Constants can help reduce the chances of this happening. In general, module-level scope is best for constants that reference elements of a control array, although local scope might be appropriate in some cases. The following constants have been given the prefix txt to denote that they reference the indexes of a text box control array.
Incorrect:
txtPhone(0).Text txtPhone(1).Text txtPhone(2).Text
Correct:
Const c_txtHomePhone = 0 Const c_txtWorkPhone = 1 Const c_txtFax = 2 txtPhone(c_txtHomePhone).Text txtPhone(c_txtWorkPhone).Text txtPhone(c_txtFax).Text
5.5 Use an application or company prefix for enumeration members.
Just as it's important to use a naming convention for variables, it's important to use a naming convention for enumeration members. You don't have to use a prefix to denote the type of an enumeration member because all members are always long integers. However, you should use a unique prefix that indicates that the values are from your application or component.
You should prefix your enumeration members with an identifier because when Visual Basic encounters an enumeration member name, it might get confused if other referenced type libraries contain the same name. For example, all of Visual Basic's system constants have the prefix vb. When you encounter a constant such as vbFixedSingle, you immediately know that the constant belongs to Visual Basic's type library. Although Visual Basic uses two-character prefixes, you should use three or four, but no more than that. If you were to use two characters, you would find it difficult to come up with an identifier that isn't used by another application or vendor. For instance, my company is called Tigerpaw Software. When we declare enumeration members, we use the prefix tps, as shown in the enumeration declaration on the following page.
Public Enum tpsPrintDestination tpsScreen = 0 tpsPrinter = 1 End Enum
Note: It's also acceptable to prefix the name of an enumeration (as well as the names of its members), as I have done in the previous example.
No application is an island. Even the simplest program uses many external libraries. To confirm this, just create a new Standard EXE and then choose References from the Project menu to see all the ActiveX components being referenced. With the increasing complexity of integrated components comes the need to be more aware of the potential for collisions between components. For this reason, give your enumeration members names that make such collisions unlikely.
Incorrect:
Public Enum BackTrackItem Account = 0 ServiceOrder = 1 Quote = 2 Contact = 3 PriceBookItem = 4 PurchaseOrder = 5 Task = 6 End Enum
Correct:
Public Enum BackTrackItem tpsAccount = 0 tpsServiceOrder = 1 tpsQuote = 2 tpsContact = 3 tpsPriceBookItem = 4 tpsPurchaseOrder = 5 tpsTask = 6 End Enum
Also correct:
Public Enum tpsBackTrackItem tpsAccount = 0 tpsServiceOrder = 1 tpsQuote = 2 tpsContact = 3 tpsPriceBookItem = 4 tpsPurchaseOrder = 5 tpsTask = 6 End Enum
5.6 Use system constants when enumerations aren't available.
Creating custom enumerations for your modules is highly encouraged, but Visual Basic has been slow to adopt them for all of its own objects. For instance, when you set the WindowState property of a form, there are only three possible values: 0 - Normal, 1 - Minimized, and 2 - Maximized. Looking in the Properties window shows you this. Each value (0, 1, and 2) has a name associated with it. These names look very much like members of an enumeration—well, in the Properties window, at least.
Although Visual Basic should use true enumerations for these properties, more often than not it doesn't. Figure 5-3 shows what the code window looks like when you attempt to change the WindowState property of a form. Notice that there is no code helper drop-down list in which you can select from a set of values.

Figure 5-3: When a parameter or property doesn't support an enumeration, you must remember the possible values; you don't get any help from Visual Basic.
See full-sized image.
Visual Basic doesn't have defined enumerations for most of its objects, but it does often support system constants for the values. System constants are global constants that are part of the Visual Basic type library. You don't have to define them or reference a library to use them because they're always available. However, since system constants don't appear in the code helper drop-down list, as enumerations do, many developers are unaware that these constants exist. Whenever you must type a numeric value as a parameter of a Visual Basic function or as the value of a standard Visual Basic object property, chances are good there's an associated system constant.
To determine whether a system constant exists for a property, type the property name (such as WindowState), place the cursor anywhere within the property text, and press F1. The help displayed for the property will usually include a list of any constants that are available, as shown in Figure 5-4.

Figure 5-4: When you have to hard-code a value for a parameter, check online Help to see whether a system constant is available.
See full-sized image.
Note: Many parameters, such as the Buttons parameter of the MsgBox statement, have associated system constants.
To use a system constant, you simply enter it as if you were referencing a constant that you've defined. For instance, to change a form to maximized, you can use a statement such as this:
Me.WindowState = vbMaximized
One way to know whether you've typed a system constant correctly is to type it in all lowercase letters. If the constant is valid, Visual Basic converts it to its proper case. If the constant remains in all lowercase letters, you've typed the name wrong and you have to correct it. Unlike enumerations, system constants can be used anywhere in code, not just with variables defined as an enumerated type. Therefore, you must be careful not to use the wrong constant because Visual Basic can't detect this type of error. Anything you can do to eliminate magic numbers is a good thing. If an enumeration is available for a procedure, use it. If not, check to see whether a system constant is defined. If that fails, consider creating your own constant to replace the magic number.
Incorrect:
With Me BorderStyle = 1 .WindowState = 0 .ScaleMode = 3 .DrawMode = 13 End With
Correct:
With Me .BorderStyle = vbFixedSingle .WindowState = vbNormal .ScaleMode = vbPixels .DrawMode = vbCopyPen End With
5.7 Use an enumeration whenever a parameter accepts a limited number of values.
Even developers who truly believe in enumerations sometimes miss the opportunity to use them. As you develop code, you might not always think about creating an enumeration because an enumeration might seem like overkill in certain situations. In general, whenever a procedure accepts a limited set of values, use an enumeration. For instance, if you have a procedure in which a parameter can use one of two values only, that parameter is a prime candidate for an enumeration.
Creating an enumeration for two values might seem excessive, but it's not. You still get the benefits of avoiding magic numbers, including reduced data entry and greater legibility. Also, if you decide to add members in the future, an enumeration will make it easier to do so. Whether the values are strings or numbers is irrelevant; you can benefit from using an enumeration in both situations.
Incorrect:
Public Sub ShowAVIFile(lngType As Long)
Correct:
Public Enum tpsAVIFile tpsFileCopy = 0 tpsFileDelete = 1 tpsFileDeleteToRecycle = 2 tpsFileNuke = 3 tpsFindComputer = 4 tpsFindFile = 5 tpsFileMove = 6 tpsSearch = 7 End Enum Public Sub ShowAVIFile(lngType As tpsAVIFile)
5.8 Validate values that are passed as enumerated types.
You must validate any value passed to a parameter declared as an enumerated type to ensure that it is acceptable. When a parameter is declared as an enumerated type, it's really a long integer with a fancy code helper drop-down list. While other developers should use the named enumeration members, they are free to pass the parameter any valid long integer. Unfortunately, Visual Basic does not have the capability to automatically require that parameters be valid members of the enumeration. I hope this is added someday; it would greatly reduce the amount of code you have to write when you use many different enumerations.
There are essentially two methods you can use to validate the data:
| • | If the values fall within a range (such as 1 to 10), use an If...End If construct to validate the data. |
| • | If the values form a discrete set that doesn't fit nicely in a range, use a Select Case construct. |
In general, unless you are validating that the value falls within an acceptable range, use the Select Case construct rather than If...End If. The Select Case construct gives you more flexibility if you need to add more values to the enumeration later.
Note: When you use Select Case, always include an Else clause to deal with an invalid value passed into the procedure.
Practical Applications
Making assumptions is one of the leading causes of errors in code. Visual Basic won't ensure that values passed to an enumerated type actually correspond to named members within the enumeration. Never assume you have good data.
5.8.1 Always validate data by using comparisons to the named members, not to magic numbers. Refrain from using magic numbers for data validation, just as you refrain from using magic numbers elsewhere in your code.
Incorrect:
Public Enum tpsPrintDestination tpsScreen = 0 tpsPrinter = 1 End Enum Public Sub PrintReport(ByVal strFileName As String, _ ByVal lngDestination As tpsPrintDestination) '* Verify that a valid location has been specified. If lngDestination < 0 Or lngDestination > 1 Then GoTo PROC_EXIT End If '* Print the specified report. End Sub
Correct:
Public Enum tpsPrintDestination tpsScreen = 0 tpsPrinter = 1 End Enum Public Sub PrintReport(ByVal strFileName As String, _ ByVal lngDestination As tpsPrintDestination) '* Verify that a valid location has been specified. If (lngDestination <> tpsScreen) And _ (lngDestination <> tpsPrinter) Then GoTo PROC_EXIT End If '* Print the specified report. End Sub
5.8.2 Use Select Case to validate that a value is a valid member of a discrete set of values. Don't forget to include the Case Else clause to handle invalid data.
Correct:
Public Sub PrintDocument(lngCallingForm As tpsCallingForm) '* Perform necessary actions based on the calling form. Select Case lngCallingForm Case Is = tpsContactView Case Is = tpsServiceOrderView Case Is = tpsCustomerInventoryView Case Else '* The value passed as the calling form parameter '* is invalid! MsgBox "An incorrect calling form " & _ "has been specified!", vbInformation GoTo PROC_EXIT End Select End Sub