Denoting a Variable s Data Type

Directives

5.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:
   Public Function IsEmailAddressValid(ByVal strEMailAddress As String) _                    As Boolean        ' Purpose   :  Determines whether the supplied e-mail       '              address has a valid format.        Dim intLocation As Integer       Dim intCounter As Integer       Dim strCharacter As String        ' Make sure we have an @ sign, some text, then a period        ' and some text.        If Not strEMailAddress Like "*@*.*" Then          GoTo PROC_EXIT       End If        ' Get the location of the @ sign.       intLocation = InStr(strEMailAddress, "@")       ' Make sure the @ sign isn't the first character.        If intLocation = 1 Then          GoTo PROC_EXIT       End If        ' Make sure there isn't more than one @ sign.        If InStr(intLocation + 1, strEMailAddress, "@") > 0 Then          GoTo PROC_EXIT       End If        ' Make sure there is no dot (period) after the @ sign.        If strEMailAddress.SubString(intLocation, 1) = "." Then          GoTo PROC_EXIT       End If        ' Get the last dot.       intLocation = InStrRev(strEMailAddress, ".")       ' Make sure there are at least two characters following the @ sign.        If intLocation > strEMailAddress.Length - 2 Then          GoTo PROC_EXIT       End If        ' Make sure alpha characters are used for the domain.        For intCounter = (intLocation) To strEMailAddress.Length - 1          If Not (LCase(strEMailAddress.Substring(intCounter, 1)) Like _                  "[a-z]") Then             GoTo PROC_EXIT          End If       Next intCounter       ' Determine whether the string contains any funky characters.        For intCounter = 0 To strEMailAddress.Length - 1          strCharacter = strEMailAddress.Substring(intCounter, 1)          Select Case strCharacter             Case Is = ".", "@", "-", "_"                ' Character is fine.              Case Else                If LCase(strCharacter) Like "[a-z]" Then                    ' Character is fine.                 ElseIf strCharacter Like "[0-9]" Then                    ' Character is fine.                 Else                    ' This character is invalid!                    GoTo PROC_EXIT                End If          End Select       Next intCounter       IsEmailAddressValid = True PROC_EXIT:    End Function 
Correct:
   Public Function IsEmailAddressValid(ByVal strEMailAddress As String) _                    As Boolean        ' Purpose   :  Determines whether the supplied e-mail        '              address has a valid format.        Dim intAtLocation As Integer       Dim intDomainDotLocation As Integer       Dim intCounter As Integer       Dim strCharacter As String        ' Make sure we have an @ sign, some text, then a period        ' and some text.        If Not strEMailAddress Like "*@*.*" Then          GoTo PROC_EXIT       End If        ' Get the location of the @ sign.       intAtLocation = InStr(strEMailAddress, "@")       ' Make sure the @ sign isn't the first character.        If intAtLocation = 1 Then          GoTo PROC_EXIT       End If        ' Make sure there isn't more than one @ sign.        If InStr(intAtLocation + 1, strEMailAddress, "@") > 0 Then          GoTo PROC_EXIT       End If        ' Make sure there is no dot (period) after the @ sign.        If strEMailAddress.SubString(intAtLocation, 1) = "." Then          GoTo PROC_EXIT       End If        ' Get the last dot.       intDomainDotLocation = InStrRev(strEMailAddress, ".")       ' Make sure there are at least two characters following the @ sign.        If intDomainDotLocation > strEMailAddress.Length - 2 Then          GoTo PROC_EXIT       End If        ' Make sure alpha characters are used for the domain.        For intCounter = (intDomainDotLocation) To strEMailAddress.Length - 1          If Not (LCase(strEMailAddress.Substring(intCounter, 1)) Like _                  "[a-z]") Then             GoTo PROC_EXIT          End If       Next intCounter       ' Determine whether the string contains any funky characters.        For intCounter = 0 To strEMailAddress.Length - 1          strCharacter = strEMailAddress.Substring(intCounter, 1)          Select Case strCharacter             Case Is = ".", "@", "-", "_"                ' Character is fine.              Case Else                If LCase(strCharacter) Like "[a-z]" Then                    ' Character is fine.                 ElseIf strCharacter Like "[0-9]" Then                    ' Character is fine.                 Else                    ' This character is invalid!                    GoTo PROC_EXIT                End If          End Select       Next intCounter       IsEmailAddressValid = True PROC_EXIT:    End Function 

5.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.

Tip

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 As Integer  ' Print a list of the fields in the Recordset. For i = 0 To rstCustomers.Fields.Count - 1    Debug.WriteLine(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 intIndex As Integer  ' Print a list of the fields in the Recordset. For intIndex = 0 To rstCustomers.Fields.Count - 1    Debug.WriteLine(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) For Each 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 or variables defined by the author). Using constants in place of magic numbers is good practice, but Microsoft Visual Basic .NET includes built-in constants for these values (ControlChars.CrLf and ControlChars.Tab, respectively). You should always use a system constant or enumeration if one is available, as discussed in Chapter 4, "Using Constants and Enumerations."

It's important when naming variables to remember 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. A valid reason for this practice no longer exists. 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
5.2.1 Avoid naming variables Temp.

One variable name you should avoid is Temp. It's easy to create variables such as lngTemp, 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:
   Public Function StripDoubleQuotes(ByVal strString As StringAs String        ' Purpose   :  Strip double quotes and replace them with a pair of       '              single quotes.       ' Accepts   :  strString by value so that changes made to it aren't       '              also made directly to the variable being passed in.       ' Returns   :  The original string, with all double quotes replaced       '              by pairs of single quotes.        Dim intTemp As Integer        ' Repeat as long as double quotes are found.        Do           ' Look for a double quote.          intTemp = InStr(1, strString, ControlChars.Quote)          ' If a double quote is found, replace it with a pair           ' of single quotes.           If intTemp > 0 Then             strString = strString.Substring(0, intTemp - 1) & _                         "''" & strString.Substring(intTemp)          End If       Loop While intTemp > 0       StripDoubleQuotes = strString PROC_EXIT:    End Function 
Correct:
   Public Function StripDoubleQuotes(ByVal strString As StringAs String        ' Purpose   :  Strip double-quotes and replace them        '              with a pair of single quotes.       ' Accepts   :  strString by value so that changes made to it aren't       '              also made directly to the variable being passed in.       ' Returns   :  The original string, with all double quotes replaced       '              by pairs of single quotes.        Dim intQuoteLocation As Integer            ' Repeat as long as double quotes are found.        Do           ' Look for a double quote.          intQuoteLocation = InStr(1, strString, ControlChars.Quote)          ' If a double quote is found, replace it with a pair           ' of single quotes.           If intQuoteLocation > 0 Then             strString = strString.Substring(0, intQuoteLocation - 1) & _                         "''" & strString.Substring(intQuoteLocation)          End If       Loop While intQuoteLocation > 0           StripDoubleQuotes = strString PROC_EXIT:    End Function 

5.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 .NET, so use mixed case (also referred to as camel casing) for all variable names. All lowercase is almost as bad as all uppercase. For instance, EmployeeAge is far better than EMPLOYEEAGE or employeeage.

Incorrect:
Dim strFIRSTNAME As String Dim strlastname  As String 
Correct:
Dim strFirstName As String Dim strLastName  As String 

5.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 your 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     As String Dim strEmployeeAddress1 As String Dim strEmployeeAddress2 As String Dim strEmployeeCity     As String Dim strEmployeeState    As String Dim strEmployeeZipcode  As String 

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.

Tip

When you have a number of related variables like this, consider creating a structure or a class to hold the values.


 

Incorrect:
Dim strEmployeeName As String Dim strEmplAdd      As String Dim strEmployeeCity As String 
Correct:
Dim strEmpName    As String Dim strEmpAddress As String Dim strEmpCity    As String 

5.5 Use qualifiers consistently.

Most projects contain variables that are related to one another. For instance, when manipulating images, you often have a source image and a destination image. 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. Table 5-1 describes common variable qualifiers.

 

Table 5-1. 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.

Old

Oldest element in a set.

New

Newest element in a set.

 

Incorrect:
Dim strFirstCustomer    As String Dim strLastCustomer     As String Dim strPreviousCustomer As String 
Correct:
Dim strCustomerFirst    As String Dim strCustomerLast     As String Dim strCustomerPrevious As String 

5.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:
   Private Sub cmdPrint_Click(ByVal sender As System.Object, _                               ByVal As System.EventArgs) _                               Handles cmdPrint.Click       ' Purpose   :  Print the report.        Static blnNotInHere As Boolean            ' Prevent this event from executing twice because of a user       ' double-clicking the button.        If Not(blnNotInHere) Then Exit Sub       blnNotInHere = False        ' Print the quote.        Call PrintQuote PROC_EXIT:       ' Reset the variable that keeps track of whether we're in here.       blnNotInHere = True    End Sub 
Correct:
   Private Sub cmdPrint_Click(ByVal sender As System.Object, _                               ByVal As System.EventArgs) _                               Handles cmdPrint.Click       ' Purpose   :  Print the report.        Static blnInHere As Boolean            ' Prevent this event from executing twice because of a user       ' double-clicking the button.        If blnInHere Then Exit Sub           blnInHere = True        ' Print the quote.        Call PrintQuote                 PROC_EXIT:       ' Reset the variable that keeps track of whether we're in here.       blnNotInHere = False        End Sub 

5.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 it 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's a simple illustration of the problems of not explicitly declaring variables:

Private Sub cmdTest_Click(ByVal sender As System.Object, _                           ByVal As System.EventArgs) Handles cmdTest.Click    strMessage = "This is a test."    Debug.WriteLine(strMesage) End Sub 

If you weren't paying close attention, you might expect Visual Basic .NET to print "This is a test." when the procedure is executed. However, Visual Basic .NET 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: s

trMessage = "This is a test."

 

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

Line 2:

Debug.WriteLine(strMesage)

 

Because strMesage has not been declared as a variable and it's not a reserved word, Visual Basic .NET 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 Objects (which is a problem unto itself, as noted in Directive 5.9). An Object 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 On 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. Because 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 On 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.

A much easier alternative to adding Option Explicit On to each module is to turn on Option Explicit in the Project Properties dialog box. Doing so won't add Option Explicit On to your existing classes and modules, but it will force the behavior in all of them nevertheless. To turn on Option Explicit at the project level, right-click the project name in Solution Explorer, choose Properties, click Build in the tree on the Property Pages that appear, and select On for Option Explicit. (See Figure 5-1.)

It's hard to fathom why, after all this time, Option Explicit is still an option.

Figure 5-1. Always ensure that Option Explicit is turned on in every project you work with.

graphics/f05ln01.jpg

If you have a large legacy project developed without using Option Explicit, turn on this feature 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.

Incorrect:
Private Sub Test()    intValue = 6    Debug.WriteLine(intValue / 2) End Sub 
Correct:
Private Sub Test()    Dim intValue As Integer = 6    Debug.WriteLine(intValue / 2) End Sub 

5.8 Declare variables with carefully chosen data types.

Visual Basic .NET 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:

Private Sub Calculate()    Dim sngFirst  As Single    Dim sngSecond As Single    Dim intResult As Integer    sngFirst = 3    sngSecond = 0.5    intResult = sngFirst + sngSecond    Debug.WriteLine(intResult) End Sub 

When this code is executed, the number 4 is printed in the Output window. The correct result should, of course, be 3.5. However, Visual Basic .NET does its best to follow your instructions and rounds the result of the calculation to fit in the Integer variable. Visual Basic .NET doesn't raise a run-time error in such situations; it simply forces the value to fit the data type (You can make Visual Basic .NET raise compile-time errors in these situations by using Option Strict, as explained in Directive 5.10.) 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, an Overflow exception occurs. (See Figure 5-2.)

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

graphics/f05ln02.jpg

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

Private Sub Calculate()    Dim bytFirst  As Byte    Dim bytSecond As Byte    Dim bytResult As Byte    bytFirst  = 150    bytSecond = 150    bytResult = bytFirst + bytSecond    Debug.WriteLine(bytResult) End Sub 

The maximum value that can be stored in a Byte variable is 256. Because bytFirst and bytSecond are each assigned the value 150, no error is generated. However, when you attempt to place the result of adding these two numbers (300) in bytResult (another Byte variable), an overflow exception occurs. In this case, changing bytResult to a Short 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 (Single to a Double, Integer to a Long) is the safest bet. In others, you might find that an even more specific data type is available, such as the Decimal data type for holding monetary values.

Incorrect:
Private Sub PrintTax(ByVal sngTaxableAmount As Single, _                      ByVal lngTaxRate As Long)    ' Purpose   :  Print the total tax (taxable amount * tax rate).    ' Accepts   :  sngTaxableAmount - the dollar value to which tax    '              applies.    '              lngTaxRate - the local tax rate.     Dim sngTax As Single    sngTax = sngTaxableAmount * lngTaxRate    Debug.WriteLine(sngTax) End Sub 
Correct:
Private Sub PrintTax(ByVal decTaxableAmount As Decimal, _                      ByVal sngTaxRate As Single)    ' Purpose   :  Print the total tax (taxable amount * tax rate).    ' Accepts   :  decTaxableAmount - the dollar value to which tax    '              applies.    '              sngTaxRate - the local tax rate.     Dim decTax As Decimal    decTax = decTaxableAmount * sngTaxRate    Debug.WriteLine(decTax) End Sub 

Table 5-2 lists the Visual Basic .NET data types.

 

Table 5-2. Visual Basic Data Types 

Data Type

Value Range

Boolean

True or False

Byte

0 to 255 (unsigned)

Char

0 through 65,535 (unsigned)

Date

0:00:00 on January 1, 0001, through 11:59:59 PM on December 31, 9999

Decimal

0 through +/-79,228,162,514,264,337,593,543,950,335 with no decimal point;

0 through +/-7.9228162514264337593543950335 with 28 places to the right of the decimal;

smallest nonzero number is +/-0.0000000000000000000000000001 (+/-1E-28).

Double

-1.79769313486231570E+308 through -4.94065645841246544E-324 for negative values; 4.94065645841246544E-324 through 1.79769313486231570E+308 for positive values.

Integer

-2,147,483,648 through 2,147,483,647

Long

-9,223,372,036,854,775,808 through 9,223,372,036,854,775,807

Object

Any type of data

Short

-32,768 through 32,767

Single

-3.4028235E+38 through -1.401298E-45 for negative values; 1.401298E-45 through 3.4028235E+38 for positive values

String

0 to approximately 2 billion Unicode characters

 

Caution

In an unprecedented move, Microsoft changed the definitions of some of the type names when moving to Visual Basic .NET! What used to be an Integer is now a Short and what used to be a Long is now an Integer. Long is now much larger than it ever was before. When you code projects in Visual Basic .NET, you must be constantly aware of these changes!


 

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 Short data type.

  • To store numbers with no decimal places but with values greater than or less than those that a Short will allow, use the Integer data type. Integers should work for most of your nondecimal variables, but in the few cases where you need more room, use a 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 most of 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 Decimal data type.

  • To store a date or a time value, use the Date data type. When you use this data type, Visual Basic .NET recognizes common date and time formats. For example, if you store the value 7/22/2002, Visual Basic .NET doesn't treat it as a simple text string; it knows that the value represents July 22, 2002.

5.9 Use the Object data type only when absolutely necessary.

Visual Basic .NET is extremely lenient on the typing of variables (unless you use Option Strict). 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 Object data type. This data type is often abused, and almost always a better alternative to the Object data type exists. When used correctly, an Object is a great tool for certain tasks. You can create Object variables that can hold any type of data. Visual Basic is even fairly smart at determining the type of data an Object holds and applying the appropriate behavior to the determined type. For instance, consider the following code:

Dim objA As Object Dim objB As Object objA = "1" objB = 2 Debug.WriteLine(objA + objB) 

When executed, the code prints 3, even though the variable objA contains a string. When Visual Basic .NET encounters the + operator (plus sign), it determines that, if at all possible, it must perform arithmetic using numbers. Visual Basic .NET evaluates the two Object 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 objA As Object Dim objB As Object objA = "1" objB = 2 Debug.WriteLine(objA & objB) 

When this code is executed, 12 is printed. The ampersand (&) character is used for string concatenation; when Visual Basic .NET encounters the ampersand, it evaluates the two variables to determine whether their values can be interpreted as Strings. Although only one of the variables (objA) contains a String, Visual Basic .NET 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 .NET can sometimes coerce strictly typed variables variables declared as specific data types but it's more likely to do this with Objects. The previous two code samples produce the same results even if the variables are declared as specific types such as String and Integer. However, compare the following two code examples:

Dim strA As String Dim strB As String strA = 1 strB = 2 Debug.WriteLine(strA + strB) 

and

Dim objA As Object Dim objB As Object objA = 1 objB = 2 Debug.WriteLine(objA + objB) 

The first code example prints 12. When Visual Basic .NET works with String variables, it treats the plus sign as a concatenation operator. However, recall that the default behavior of the + operator is for arithmetic. In the second example, Visual Basic .NET looks at the Object variables to see whether 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 plus sign for string concatenation; use an ampersand.


 

The simple fact that Visual Basic .NET treats data in an Object variable differently at different times should be enough to dissuade you from using an Object when it's not necessary. Objects have other pitfalls as well. For instance, they consume more memory than any other Visual Basic .NET data types. Not only do Objects consume more memory than other data types, but also manipulating the data within them is almost always slower than performing the same manipulation with strictly typed variables.

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

Dim MyVariable Dim MyVariable As Object 

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 an Object. The following definitions yield the same result:

Private Function MyFunction() Private Function MyFunction() As Object 

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 Object, explicitly declare the variable or Function procedure by using As Object.

In previous versions of Visual Basic, declaring multiple variables on a single line meant that the last variable declared would have the specified type and all others on the line would be Variants, the catchall equivalent of the Object type in Visual Basic .NET. Consider this next statement:

Dim VariableA, VariableB, VariableC As String 

In previous versions of Visual Basic, the first two variables would be Variants and only the last variable would be a String. This behavior has been changed for the better in Visual Basic .NET, and now all variables declared on the same line have the same defined data type. However, I recommend that you still dimension one variable per line for enhanced readability.

5.10 Use Option Strict to enforce strict typing.

Using Option Explicit as explained in Directive 5.7 should be considered the minimum level of control you should exhibit over your variables. Visual Basic .NET includes a new option that is even more powerful Option Strict. When Option Strict is turned on, Visual Basic .NET enforces strict typing throughout your code and restricts implicit conversions to only widening conversions (moving data to a type with larger precision). Earlier, I discussed how Visual Basic .NET would coerce data, such as treating a string like a number. When Option Strict is turned on, Visual Basic won't coerce such data; instead you must explicitly convert data from one type to another. For example, with Option Strict turned off, this is a perfectly legitimate statement:

Dim intTotal As Integer = Val(txtInput.Text) 

However, with Option Strict turned on, this statement would generate the compile-time error "Option Strict On disallows implicit conversions from 'Double' to 'Integer'." This occurs because the Val() function converts Strings to Doubles, and Visual Basic .NET won't allow an implicit conversion from a Double to an Integer because of the potential of data loss. To explicitly perform such a conversion, you'd have to modify the statement like this:

Dim intTotal As Integer = CInt(txtInput.Text) 

or

Dim intTotal As Integer CType(txtInput.Text, Integer

Be aware that this does not make the code bulletproof. For example, if a user typed a number into the text box that was larger than would fit in an Integer, you could receive an Overflow exception at run time. However, by choosing to convert the value of the text box to an Integer, you have assumed responsibility, as opposed to letting Visual Basic .NET do what it thinks is best. As you write your explicit conversions, take the opportunity to think through what you're trying to accomplish and code accordingly.

As I mentioned, with Option Strict turned on, Visual Basic .NET will perform implicit widening conversions for you. For example, the following statements are perfectly legitimate, as a Double can hold the value of a Single with no change of data loss. Table 5-3 lists the widening conversions found in Visual Basic .NET. Visual Basic .NET will make these implicit conversions on your behalf.

Dim sngMySingle As Single Dim dblMyDouble As Double dblMyDouble = sngMySingle 

To turn on Option Strict, right-click the project name in Solution Explorer, choose Properties, click Build in the tree on the Property Pages that appear, and select On for Option Explicit. (Refer back to Figure 5-1 on page 72.) As with Option Explicit, if you turn on Option Strict at the project level, you don't have to include the statement at the top of your classes or modules.

Note

In order for Option Strict to work, Option Explicit has to be in effect, so if you turn on Option Strict, Option Explicit is also enforced, regardless of the setting in the Project property pages.


 

 

Table 5-3. Implicitly Widening Conversions 

Data Type

Widens to Data Types

Byte

Byte, Short, Integer, Long, Decimal, Single, Double

Short

Short, Integer, Long, Decimal, Single, Double

Integer

Integer, Long, Decimal, Single, Double

Long

Long, Decimal, Single, Double

Decimal

Decimal, Single, Double

Single

Single, Double

Double

Double

Any enumerated type

Its underlying integer type and any type to which that widens

Char

Char, String

Any type

Object

Any derived type

Any base type from which it's derived

Any type

Any interface it implements

Nothing

Any data type or object type

 

Programming with Option Strict turned on requires additional code and thought on your part. However, the benefits are huge, and the result is the creation of some of the most solid code you can write. Converting projects from earlier versions of Visual Basic so that they compile with Option Strict turned on could be a mammoth task, and you might choose not to because of the amount of work involved. I can understand this. However, when developing new projects, you should always turn on Option Strict; you will realize the benefits.

5.11 Minimize variable scope.

The scope of a variable is its visibility to other procedures and modules. Visual Basic has always offered three levels of scope: procedural, module, and global. Visual Basic .NET adds a new level called block. These levels are explained in Table 5-4.

 

Table 5-4. Levels of Scope in Visual Basic

Scope

Description

Global

The variable is declared as Public within a standard module. The variable is visible to all procedures within all modules of the project.

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.

Procedural

The variable is declared within a procedure and therefore can be seen and used only within the procedure. Variables with procedural scope are often called local variables.

Block

The variable is declared within a coding block, such as an If End If or Do Loop block. The module exists within and is visible to only code within the same block.

 

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, rather than using a higher-scoped variable.

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 forever. 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.

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 the user double-clicking, 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.

   Private Sub grdListFind_DblClick()       ' Purpose   :  Display the selected record when the user       '              double-clicks a row on the grid.        Static st_blnInHere As Boolean            ' Protect this event from reentrancy caused by an overzealous       ' user.        If st_blnInHere Then Exit Sub        ' Flag that we're in here.       st_blnInHere = True           Call ShowSelectedRecord PROC_EXIT:       st_blnInHere = False    End Sub 

When you define variables, remember to use a scope prefix to clearly indicate the scope of the variable. Table 5-5 lists the possible prefixes. (For more information on naming conventions and Hungarian prefixes, see Chapter 3.)

 

Table 5-5. 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 (This includes variables with block scope.)

 

 

5.12 Use initializers whenever possible.

Visual Basic .NET includes a new feature that allows you to initialize a variable's value at the time it's declared. This reduces the number of code statements in a procedure and increases clarity, and therefore whenever you need to initialize a new variable to a specific value, you should use an initializer.

Incorrect:
Dim intOrderQuantity As Integer intOrderQuantity = c_DefaultOrderQuantity 
Correct:
Dim intOrderQuantity As Integer = c_DefaultOrderQuantity 

5.13 Concatenate strings by using an ampersand.

A common mistake that new Visual Basic .NET 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 does let you use the plus sign for concatenation, but you should never do this; the ampersand is the official symbol for concatenating strings in Visual Basic .NET. 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 .NET performs arithmetic rather than concatenation. Consider the following code:

Dim strFirst  As String Dim strSecond As String strFirst  = "1" strSecond = "2" Debug.WriteLine(strFirst + strSecond) 

When this code is executed, 12 is printed in the Output window.

Compare the previous code to the following:

Dim strFirst  As String Dim sngSecond As Single strFirst = "1" sngSecond = "2" Debug.WriteLine(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 As String Dim strLastName  As String Dim strFullName  As String strFirstName = "Ethan" strLastName  = "Foxall" ' Concatenate the first and last names into a complete name. strFullName = strFirstName + " " + strLastName 
Correct:
Dim strFirstName As String Dim strLastName  As String Dim strFullName  As String strFirstName = "Ethan" strLastName  = "Foxall" ' Concatenate the first and last names into a complete name. strFullName = strFirstName & " " & strLastName 

Tip

If you're performing many concatenations in a given situation, consider using String.Concat() for faster performance.


 

5.14 Use a string's length property to determine whether it's empty.

It's amazing how often small changes can impact an application in big ways. This is true when working with strings. When you need to determine whether a string is empty (a common task), most programmers compare the string to a "", as in:

If strMyString = "" Then ... 

However, checking the length of the string is actually much faster up to three times as fast! So, when checking whether a string is empty, compare its length to 0 like this:

If strMyString.Length = 0 Then ... 


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

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