Using Enumerations

Directives

9.1 Use If Then Else when the decision is based on one condition being True or False.

If Then is well suited for making a decision based on the evaluation of a single condition, and it's the most commonly used decision construct. If Then has the following syntax:

If  condition  Then  statement 

or

If  condition  Then    [statements] End If 

If condition evaluates to True, the statement or statements are executed. If condition evaluates to False, the statement or statements are not executed. Expressions can be simple or complex. Regardless of complexity, the expression used for condition must evaluate to True or False. For instance, all the following expressions are valid for condition.

sngCoordinate >= 8 strFirstName = "Adam" blnInHere 

Although at first glance the last item doesn't appear to be a condition, remember that a condition always evaluates to either True or False. Because blnInHere refers to a Boolean variable (as denoted by its prefix), blnInHere inherently evaluates to True or False and is therefore a valid condition.

Note

If condition evaluates to a numerical value, Microsoft Visual Basic interprets the result as True or False; a zero value is considered False, and all nonzero numeric values are considered True.


 

To execute a statement or a set of statements when condition evaluates to False, use an Else statement. All statements between Else and End If are executed when condition evaluates to False. All statements between If Then and Else execute only if condition evaluates to True; never do both sets of statements execute in a single pass through an If End If construct.

Practical Applications
9.1.1 Consider using End If, even if only one statement is executed.

If only one statement executes when condition evaluates to True, the statement can be put on the same line as If and End If can be omitted. However, to make the code more legible, you might consider placing the statement on its own line and closing the construct with End If.

Incorrect:
' If locking conflicts were encountered, build this information ' into the message string. If lngLockingConflicts > 0 Then strMessage = strMessage & ", " & _                                 lngLockingConflicts & _                                 " contact(s) could not be added " & _                                 "due to locking conflicts!" 
Correct:
' If locking conflicts were encountered, build this information ' into the message string. If lngLockingConflicts > 0 Then    strMessage = strMessage & ", " & lngLockingConflicts & _                 " contact(s) could not be added due to " & _                 "locking conflicts!" End If 
9.1.2 Don't assume that Visual Basic will short-circuit a compound condition.

When creating an If Then decision structure, it's possible to create a compound condition composed of multiple smaller conditions. Consider the following code:

If Len(rstContacts.Source) > 0 And Not (rstContacts.EOF) Then    rstContacts.Delete() End If 

This decision structure attempts to delete a record if the Recordset's EOF (end-of-file) property is False. First, the If statement makes sure that the Recordset's source has been set, and then it makes sure that the EOF property is False. This code, however, has the potential to generate a run-time error.

If rstContacts doesn't have its Source set (Len(rstContacts.Source) = 0), you might think that Visual Basic wouldn't evaluate the second part of the condition, which is rstContacts.EOF. With an And compound condition, if either of the individual conditions evaluates to False, the entire condition evaluates to False. Therefore, with the first part of the compound condition being False, there's really no need to evaluate the second part. Unfortunately, this is not how Visual Basic behaves: Visual Basic evaluates each component of a compound condition regardless of whether it's necessary to do so. In this example, rstContacts indeed doesn't have its Source set, and attempting to determine whether the Recordset's EOF property is True causes a run-time error.

Fortunately, Visual Basic .NET introduced two new comparison operators that perform the evaluation of expressions (from left to right) in a compound condition only until the value of the compound condition is clear. The remaining individual conditions aren't evaluated and therefore cannot cause run-time exceptions. This behavior is called short-circuiting. To short-circuit the evaluation of an expression, you have to use one of the two new comparison operators: AndAlso (use in place of And) and OrElse (use in place of Or).

Incorrect:
Private Function FirstContactsState() As String     ' Purpose   :  Return the state of the first contact in the    '              Contacts table.    ' Returns   :  The state of the first contact, otherwise an empty     '              string.     Dim strSQL As String    Dim rstContacts As New ADODB.Recordset()    ' Retrieve the contacts, ordered by their name.    strSQL = "SELECT * FROM Contacts ORDER BY [ContactName];"    ' Create the recordset of contacts.    rstContacts.Open(strSQL, m_cnADOConnection, _                     ADODB.CursorTypeEnum.adOpenDynamic, _                     ADODB.LockTypeEnum.adLockOptimistic)    ' Make sure we're not at the end of file, and that the first     ' contact has a state.     If Not (rstContacts.EOF) And _          rstContacts.Fields("ContactState").Value <> "" Then       FirstContactsState = rstContacts.Fields("ContactState").Value    End If End Function 
Correct:
Private Function FirstContactsState() As String     ' Purpose   :  Return the state of the first contact in the    '              Contacts table.    ' Returns   :  The state of the first contact, otherwise an empty     '              string.     Dim strSQL As String    Dim rstContacts As New ADODB.Recordset()    ' Retrieve the contacts, ordered by their name.    strSQL = "SELECT * FROM Contacts ORDER BY [ContactName];"    ' Create the recordset of contacts.    rstContacts.Open(strSQL, m_cnADOConnection, _                     ADODB.CursorTypeEnum.adOpenDynamic, _                     ADODB.LockTypeEnum.adLockOptimistic)    ' Make sure we're not at the end of file, and that the first     ' contact has a state.     If Not (rstContacts.EOF) AndAlso _          rstContacts.Fields("ContactState").Value <> "" Then          FirstContactsState = rstContacts.Fields("ContactState").Value    End If End Function 

9.2 Use Select Case when comparing a non-Boolean expression to a variety of possible values.

When evaluating an expression that has only two possible values (True or False), If Then is the best decision construct to use. If you must take some action when the expression evaluates to False as well as when it evaluates to True, add an Else clause. Because there's no easy way to use If Then to compare an expression to more than two possible values, Select Case becomes the best choice in that situation. A typical Select Case construct has the following syntax:

Select Case  testexpression    [Case  expressionlist1       [statementblock-1]]    [Case  expressionlist2       [statementblock-2]]        [Case Else       [statementblock-n]] End Select 

Note

Select Case can be used in many advanced ways, including putting multiple result values on a single Case line. My intent in this chapter is to show you when to use it and how to use it properly, not to show you the many advanced ways it can be employed.


 

At times, you might find that you want to perform an action only when a given condition is True. If the condition evaluates to False, you want to evaluate a second condition and execute code based on the results of this new condition. You can create complex decision structures by using this method. The following shows a skeleton of just such a decision construct:

If  condition1  Then     ElseIf  condition2  Then     ElseIf  condition3  Then     Else     End If 

Notice that these conditions can be entirely unrelated. This is in contrast to the Select Case structure where the conditions are formed by successively comparing one (typically non-Boolean) test expression to each of the expressions in the Case statements. If you find that you're evaluating the same expression and comparing it to a variety of possible values, you should use Select Case rather than If Then ElseIf End If.

Incorrect:
' Set the pushed state of the proper option button. If rstTask.Fields("Type").Value = "Phone Call" Then    optPhoneCall.Checked = True ElseIf rstTask.Fields("Type").Value = "Appointment" Then    optAppointment.Checked = True ElseIf rstTask.Fields("Type").Value = "To-do" Then    optTodo.Checked = True End If 
Correct:
' Set the pushed state of the proper option button. Select Case rstTask.Fields("Type").Value    Case Is = "Phone Call"       optPhoneCall.Checked = True    Case Is = "Appointment"       optAppointment.Checked = True    Case Is = "To-do"           optTodo.Checked = True    Case Else        ' No other values are expected. End Select 

Tip

You can use Select Case in ways that might not be immediately apparent. For instance, when you have a number of option buttons on a form, you often need to determine which of the option buttons is selected (Checked = True). If you consider True as your test expression, you can create a fairly nifty construct to determine which option button is selected:

' Determine which option button is selected. Select Case True    Case Is = optDragCopyFile.Checked           Case Is = optDragMoveFile.Checked           Case Is = optDragCreateShortcut.Checked           Case Else       ' There are no further option buttons to test. End Select 


 

Practical Applications
9.2.1 Always include a Case Else with every Select Case construct, even when it's not needed.

Generally, you design a Select Case construct such that it handles every possible value of testexpression. Case Else is useful for creating code to execute when none of your specifically expected results are encountered. However, many developers often leave out Case Else if they've included Case statements for all expected results. In general, it's a good idea to always include Case Else. If you want to ignore the results not specifically accounted for in the Case statements, simply note this in a comment in the Case Else clause. If you firmly believe that no value would cause Case Else to execute that is, if all possible results are accounted for in the Case statements consider raising an error in the Case Else clause. That way, if a value slips through which could happen if additional possible results are created during future development and the Select Case structure is not properly updated you'll know about it. Always including a Case Else clause makes your code more self-documenting, and you don't force other developers to guess your intentions for handling results not specifically accounted for in Case statements.

Incorrect:
Select Case m_intActiveSearchType    Case Is = c_ListSearch       txtSearch.Text = grdListFind.Columns(c_ListFindName)    Case Is = c_PhoneSearch       txtSearch.Text = grdPhones.Columns(c_PhoneFindName)    Case Is = c_PriceBookSearch       txtSearch.Text = grdPriceBook.Columns(c_PriceBookFindName) End Select 
Correct:
Select Case m_intActiveSearchType    Case Is = c_ListSearch       txtSearch.Text = grdListFind.Columns(c_ListFindName)    Case Is = c_PhoneSearch       txtSearch.Text = grdPhones.Columns(c_PhoneFindName)    Case Is = c_PriceBookSearch       txtSearch.Text = grdPriceBook.Columns(c_PriceBookFindName)    Case Else        ' All possible values should be covered, but just in case...       MessageBox.Show("Unexpected value encountered for " & _                       "m_intActiveSearchType in " & _                       Me.Name & ": " & CStr(m_intActiveSearchType), _                       "Error", MessageBoxButtons.OK, _                       MessageBoxIcon.Exclamation) End Select 
9.2.2 Use an intelligible ordering sequence for all Case statements.

The order of the various Case statements in a Select Case construct might seem superficial, but it's often quite important. When ordering the statements, consider speed and readability. When a Select Case statement is encountered, the Case statements are evaluated in their listed order until a condition is found to be True. In a large list of items, and when speed is the primary concern, you might consider putting the most frequently expected values at the top of the Case list. More often than not, however, speed is second in importance to readability and ease of maintenance. In these cases, put the list of items in alphabetical or numerical order, which makes it easier to debug the code and to add new values to the Case list.

Incorrect:
Select Case rstBilling.Fields("Basis").Value    Case Is = "Metered"       Call ComputeMeteredContract()    Case Is = "Hourly"       Call ComputeHourlyContract()    Case Is = "Units"       Call ComputeUnitsContract()    Case Is = "Incidents"       Call ComputeIncidentsContract() End Select 
Correct:
Select Case rstBilling.Fields("Basis").Value    Case Is = "Hourly"       Call ComputeHourlyContract()    Case Is = "Incidents"       Call ComputeIncidentsContract()    Case Is = "Metered"       Call ComputeMeteredContract()    Case Is = "Units"       Call ComputeUnitsContract()    Case Else        ' No other values are expected. End Select 
9.2.3 Don't create a Case expression that will never produce a True result.

When creating Select Case structures that evaluate a numeric value, you can create Case statements that never produce a True result. This usually occurs as a result of incorrectly ordering the Case statements, causing an earlier statement to evaluate to True before a later statement is encountered. Notice how the Case Is <= 0 statement in the incorrect code below never evaluates to True. A value of less than 0 causes the preceding Case statement (Case Is <= 5) to evaluate to True, stopping all further evaluations.

Incorrect:
Select Case sngTaxRate    Case Is <= 5           Case Is <= 0           Case Is <= 10           Case Else        End Select 
Correct:
Select Case sngTaxRate    Case Is <= 0           Case Is <= 5           Case Is <= 10           Case Else        End Select 

9.3 Use end-of-line comments to add clarity to nested decision structures.

Chapter 7, "Commenting Code," describes the proper way to comment a program in great detail. Although Chapter 7 argues that inline comments are superior to end-of-line comments, end-of-line comments are appropriate at times for example, when nested decision structures significantly complicate code. In long procedures, it can be difficult to determine which end-of-construct statement (End Select or End If) corresponds to which beginning-of-construct statement. In these situations, use an end-of-line comment after the last statement of each decision structure to state which decision construct the closing statement belongs to.

Note

If you prefer, you can use end-of-line comments after the terminating statements of all your decision structures, regardless of whether they're part of a nested group.


 

Incorrect:
' Make sure the user has entered a positive number of hours. If otnumHours.Value <= 0 Then    MessageBox.Show("This is an hourly contract. You must enter " & _                    "a positive number of hours.", _                    MessageBoxButtons.OK, MessageBoxIcon.Information)    otnumHours.Focus() Else     ' Determine whether the user has entered a rate.     If otnumRate.Value > 0 Then        otnumPrice.Value = otnumHours.Value * otnumRate.Value    Else       If otnumPrice.Value > 0 Then           ' No rate entered; check whether a contract price is set.          ' If a contract price is set, figure the rate based on the           ' hours entered.          otnumRate.Value = otnumPrice.Value / otnumHours.Value       End If    End If End If 
Correct:
' Make sure the user has entered a positive number of hours. If otnumHours.Value <= 0 Then    MessageBox.Show("This is an hourly contract. You must enter " & _                    "a positive number of hours.", _                    MessageBoxButtons.OK, MessageBoxIcon.Information)    otnumHours.Focus() Else     ' Determine whether the user has entered a rate.     If otnumRate.Value > 0 Then        otnumPrice.Value = otnumHours.Value * otnumRate.Value    Else       If otnumPrice.Value > 0 Then           ' No rate entered; check whether a contract price is set.          ' If a contract price is set, figure the rate based on the           ' hours entered.          otnumRate.Value = otnumPrice.Value / otnumHours.Value       End If    ' otnumPrice.Value > 0     End If       ' otnumRate.Value > 0  End If          ' otnumHours.Value <= 0 

9.4 Format expressions for accurate evaluation and ease of understanding.

The evaluation of expressions is at the heart of creating decision structures. Most expressions can be written in multiple ways. Properly formatting an expression reduces the possibility of errors in your code and increases its readability. Because most expressions used in decision structures evaluate to a Boolean value (True or False), correctly working with Boolean expressions is crucial.

Practical Applications
9.4.1 Never compare a Boolean expression to True or False.

This seems like a basic principle, but it's one that is often violated. Boolean values are True or False, so there's no need to compare them to True or False. The incorrect code below came from a well-respected Visual Basic magazine. Lack of a naming convention aside, this procedure suffers from a case of overcomplication. BOF and EOF are properties of the Recordset object that indicate that the Recordset is at beginning-of-file or end-of-file, respectively. Each can be either True or False. Because they are inherently Boolean values, comparing them directly to True or False makes the code cluttered and can decrease performance.

Incorrect:
Public Function IsEmptyRecordset(ByRef rs As Recordset) As Boolean    IsEmptyRecordset = ((rs.BOF = TrueAnd (rs.EOF = True)) End Function 
Correct:
Public Function IsEmptyRecordset(ByRef rs As Recordset) As Boolean    IsEmptyRecordset = rs.BOF And rs.EOF End Function 
9.4.2 Create Boolean variable names that reflect the positive rather than the negative.

A classic case of overcomplicating a procedure is creating a Boolean variable name that reflects the negative of some condition. Basing decisions on such variables adds an unnecessary layer of complexity for instance, why call a variable blnNotLoaded when blnLoaded works just as well and is easier for the mind to deal with? When you work with the negative, you increase the chances for errors in your code because you might not catch problems as you write them. This isn't not like using double negatives in a sentence get it? If you must deal with the negative, use Not on the positive form of the variable rather than using the negative form of the variable.

Incorrect:
Dim blnInvalidTemplate As Boolean     ' Attempt to open the template. The function OpenTemplate returns ' success or failure. blnInvalidTemplate = Not (OpenTemplate(strTemplateName))   ' If the template is invalid, get out. If blnInvalidTemplate Then    GoTo PROC_EXIT End If 
Correct:
Dim blnValidTemplate As Boolean     ' Attempt to open the template. The function OpenTemplate returns ' success or failure. blnValidTemplate = OpenTemplate(strTemplateName)   ' If the template is invalid, get out. If Not (blnValidTemplate) Then    GoTo PROC_EXIT End If 
9.4.3 Use parentheses in expressions for clarity, even when they're not required.

Parentheses are used in algebraic expressions to override the traditional order of operations. For instance, standard order of operations (order of precedence) dictates that multiplication takes place before addition. So, the statement Debug.WriteLine(1 + 5 * 6) prints the value 31. To override this behavior, you use parentheses. Items in parentheses are evaluated first. For instance, Debug.WriteLine((1 + 5) * 6) prints 36. Although you don't have to provide parentheses if you want to use the traditional order of operations, you should use them anyway to add clarity to complicated expressions.

Incorrect:
' Compute the height and width of the editable area. m_sngEditWidth = m_sngImageWidth * m_intMagnification + 1 m_sngEditHeight = m_sngImageHeight * m_intMagnification + 1 
Correct:
' Compute the height and width of the editable area. m_sngEditWidth = (m_sngImageWidth * m_intMagnification) + 1 m_sngEditHeight = (m_sngImageHeight * m_intMagnification) + 1 
9.4.4 Make code flow obvious.

When writing decision structures, make the flow of the code as obvious as possible. Pen and paper shouldn't be required for someone to figure out your intentions. The incorrect code below was also culled from a well-known Visual Basic publication. Can you easily tell exactly what's happening here? The code is setting the Cancel parameter equal to the result of the MsgBox function. No the return value of the function is compared to vbCancel, and the result (True or False) is what's stored in the Cancel parameter. Why make the reader work so hard? The revised code performs the same function without any real loss of performance, and it's much easier to understand.

Incorrect:
Private Sub fclsMain_Closing(ByVal sender As Object, _                ByVal As System.ComponentModel.CancelEventArgs) _                Handles MyBase.Closing    ' Get confirmation before closing the form.    e.Cancel = (MsgBox("Quit Now?", vbOKCancel Or _                vbQuestion, "Confirmation Demo") = vbCancel) End Sub 
Correct:
Private Sub fclsMain_Closing(ByVal sender As Object, _                ByVal As System.ComponentModel.CancelEventArgs) _                Handles MyBase.Closing    ' Get confirmation before closing the form.     If MsgBox("Quit Now?", vbOKCancel Or vbQuestion, _              "Confirmation Demo") = vbCancel Then       e.Cancel = True    End If End Sub 

9.5 Use GoTo only when no other alternatives exist.

Although the venerable GoTo statement has been used to force execution to a specific line in a procedure for quite some time, GoTo statements make code difficult to follow because they often redirect the execution path in unintuitive ways. GoTo used to be perfect for jumping to a single exit point. However, the new Try Catch Finally construct now gives you structured exception handling and single exit points all rolled into one. In general, there's almost always a better way to write a process than by using GoTo. Rarely, if ever, should a GoTo statement send code execution back in a procedure. Often, when a GoTo statement sends execution backward, some sort of standard looping construct would be a better solution. The following code illustrates how code with a GoTo statement can be better written as a Do loop.

Incorrect:
   Private Function StripDoubleQuotes(ByVal strString As StringAs String        ' Purpose   :  Locate all double quotes within a string and change       '              them to single quotes.       ' Accepts   :  strString - the string in which to search for        '              double quotes.       ' Returns   :  The string passed here as strString, with double       '              quotes replaced by single quotes.        Dim intLocation As Integer       Const c_SingleQuote = "'" START_CHECK:       ' Look for a double quote.       intLocation = InStr(strString, ControlChars.Quote)       ' If a double quote is found, replace it with a single quote.        If intLocation > 0 Then           ' A double quote has been found. Replace it with          ' a single quote.          Mid$(strString, intLocation, 1) = c_SingleQuote          GoTo START_CHECK       End If PROC_EXIT:       ' No more double quotes were found. Return the new string.       Return strString    End Function 
Correct:
   Private Function StripDoubleQuotes(ByVal strString As StringAs String        ' Purpose   :  Locate all double quotes within a string and change       '              them to single quotes.       ' Accepts   :  strString - the string in which to search for        '              double quotes.       ' Returns   :  The string passed here as strString, with double       '              quotes replaced by single quotes.        Dim intLocation As Integer       Const c_SingleQuote = "'"       Do           ' Look for a double quote.          intLocation = InStr(strString, ControlChars.Quote)          ' If a double quote is found, replace it with a single quote.           If intLocation > 0 Then              ' A double quote has been found. Replace it with             ' a single quote.             Mid$(strString, intLocation, 1) = c_SingleQuote          End If       Loop While intLocation > 0 PROC_EXIT:       ' No more double quotes were found. Return the new string.       Return strString    End Function 
Practical Application
9.5.1 Use all uppercase letters for GoTo labels.

GoTo statements make code hard to read. Reduce the amount of effort required to scan a procedure for GoTo labels by using all uppercase letters for those labels.

Incorrect:
   Private Sub DoSomething()       ' Do something here. Proc_Exit:    End Sub 
Correct:
   Private Sub DoSomething()       ' Do something here. PROC_EXIT:    End Sub 



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