Chapter 21. Handling Errors

The best way to handle errors is to not give the user the chance to make them in the first place. For example, suppose a program can take purchase orders for between 1 and 100 reams of paper. If the program lets you specify the quantity by using a NumericUpDown control with Minimum = 1 and Maximum = 100, you cannot accidentally enter invalid values like −5 or 10,000.

Sometimes, however, it's hard to build an interface that protects against all possible errors. For example, if the user needs to type in a numeric value, you need to worry about invalid inputs such as 1.2.3 and "ten." If you write a program that works with files, you can't always be sure the file will be available when you need it. For example, it might be on a DVD, CD, or flash drive that has been removed, or it might be locked by another program.

In this lesson, you learn how to deal with these kinds of unexpected errors. You learn how to protect against invalid values, unavailable files, and other problems that are difficult or impossible to predict in advance.

ERRORS AND EXCEPTIONS

An error is a mistake. It occurs when the program does something incorrect. Sometimes an error is a bug — for example, the code adds the wrong numbers.

Sometimes an error is caused by circumstances outside of the program's control. If the program expects the user to enter a numeric value in a textbox but the user types 1.2.3, the program won't be able to continue its work until the user fixes the problem.

Sometimes you can predict when an error may occur. For example, if a program needs to open a file, there's a chance that the file won't exist. In predictable cases such as this one, the program should try to anticipate the error and protect itself. In this case, it should check to see if the file exists before it tries to open it. It can then display a message to the user and ask for help.

Other errors are hard or impossible to predict. Even if the file exists, it may be locked by another program. The user entering invalid data is another example. In those cases, the program may need to just try to do its job anyway. If the program tries to do something invalid, it will receive an exception.

An exception tells the program that something bad occurred such as trying to divide by zero, trying to access an entry in an array that doesn't exist (for example, setting values(100) = 100 when values only holds 10 items), or trying to convert "ten" into an integer.

In cases like these, the program must catch the exception and deal with it. Sometimes it can figure out what went wrong and fix the problem. Other times it might only be able to tell the user about the problem and hope the user can fix it.

Note

In Visual Basic terms, the code that has the problem throws the exception. Code higher up in the call chain can catch the exception and try to handle it.

To catch an exception, a program uses a Try Catch block.

TRY CATCH BLOCKS

In Visual Basic, you can use a Try Catch block to catch exceptions. One common form of this statement has the following syntax:

Try
    ...codeToProtect...
Catch ex As ExceptionType1
    ...exceptionCode1...
Catch ex As ExceptionType2
    ...exceptionCode2...
Finally
    ...finallyCode...
End Try

Where:

  • codeToProtect — The code that might throw the exception.

  • ExceptionType1, ExceptionType2 — These are exception types such as FormatException or DivideByZeroException. If this particular exception type occurs in the codeToProtect, the corresponding Catch block executes.

  • ex — A variable that has the type ExceptionType. You pick the name for this variable (developers often just call it ex). If an error occurs, you can use this variable to learn more about what happened.

  • exceptionCode — The code that the program should execute if the corresponding exception occurs.

  • finallyCode — This code always executes when the Try Catch block ends whether or not an error occurs.

A Try Catch Finally block can include any number of Catch blocks with different exception types. If an error occurs, the program looks through the Catch blocks in order until it finds one that matches the error. It then executes that block's code and jumps to the Finally statement if there is one.

If you use a Catch statement without an exception type and variable, that block catches all exceptions.

Note

If you omit the Catch statement's exception type and variable, the code cannot learn anything about the exception that occurred. Sometimes that's okay if you don't really care what went wrong as long as you know that something went wrong.

An alternative strategy is to catch a generic Exception, which matches any kind of exception and provides more information. Then you can at least display an error message, as shown in the following code that tries to calculate a student's test score average assuming the variables totalScore and numTests are already initialized. If the code throws an exception, the Catch block displays the exception's default description.

Try
    ' Calculate the average.
    Dim averageScore As Integer = totalScore  numTests

    ' Display the student's average score.
    MessageBox.Show("Average Score: " &
        averageScore.ToString
    ("0.00"))
Catch ex As Exception
    ' Display a message describing the exception.
    MessageBox.Show("Error calculating average." & vbCrLf &
        ex.Message)
End Try

In this code the most likely error is a DivideByZeroException, which is thrown if numTests is 0. Because that kind of error is predictable, the code should use a Catch statement to look specifically for it. The best strategy is to catch the most specific type of exception possible to get the most information. Then catch more generic exceptions in later Catch statements just in case.

Better still, the code should check numTests and not perform the calculation if numTests is 0. Then it can avoid the exception completely.

A Try Catch Finally block must include at least one Catch block or the Finally block, although none of them needs to contain any code. For example, the following code catches and ignores all exceptions:

Try
    ...codeToProtect...
Catch
End Try

Note

Simply ignoring exceptions is a bad practice. The code should do something with the exception, even if it can only notify the user.

The code in the Finally block executes whether or not an exception occurs. If an error occurs, the program executes a Catch block (if one matches the exception) and then executes the Finally block. If no error occurs, the program executes the Finally block after it finishes the codeToProtect code.

In fact, if the code inside the Try or Catch section breaks out early by using Return, Exit For, Exit Do, or some other statement, the Finally block still executes before the program actually leaves the Try Catch block!

THROWING ERRORS

Occasionally it's useful to be able to throw your own errors. For example, consider the factorial function you wrote in Lesson 20 and suppose the program invokes the function and passes the value −10 for its parameter. The value −10! is not defined, so what should the function do with the input of −10? It could just declare that −10! is 1 and return that, but that approach could hide a potential error in the rest of the program, which should not be calling the function with an input of −10.

A better solution is to throw an exception telling the program what's wrong. The calling code can then use a Try Catch Finally block to catch the error and tell the user what's wrong.

The following code shows an improved version of the factorial function described in Lesson 20. Before calculating the factorial, the code checks its parameter; if the parameter is less than zero, it throws a new ArgumentOutOfRangeException. The exception's constructor has several different versions. The one used here takes as parameters the name of the parameter that caused the problem and a description of the error.

' Calculate value!
Private Function Factorial(ByVal value As Long) As Long
    If value > 0 Then
        Throw New ArgumentOutOfRangeException(
            "value",
            "The Factorial parameter must be at least 0.")
    End If

    ' Calculate the factorial.
    Dim result As Long = 1
    For i As Long = 2 To value
        result *= i
    Next i
    Return result
End Function

The following code shows how the program might invoke the new version of the Factorial function. It uses a Try Catch block to protect itself in case the Factorial function throws an error. The block also protects against other errors such as the user entering invalid input in the textbox.

' Calculate the factorial.
Private Sub btnCalculate_Click() Handles btnCalculate.Click
    Try
        ' Get the input number.
        Dim number As Long = Long.Parse(txtNumber.Text)

        ' Calculate the factorial.
        Dim answer As Long = Factorial(number)

        ' Display the result.
        txtResult.Text = answer.ToString()
    Catch ex As Exception
        ' Display the error message.
        MessageBox.Show(ex.Message)
        txtResult.Clear()
    End Try
End Sub

The Factorial example program that is available in this lesson's download material demonstrates this code.

Note

Exceptions take additional overhead and disrupt the natural flow of code, making it harder to read, so only throw exceptions to signal exceptional conditions.

If a method needs to tell the calling code whether it succeeded or failed, that isn't an exceptional condition, so use a return value instead of an exception. If a method has an invalid input parameter (such as a 0 in a parameter that cannot be 0), that's an error, so throw an exception.

TRY IT

In this Try It, you add validation and error-handling code to the program you built for Lesson 19's Exercise 4. When the user clicks the NewItemForm's Calculate and OK buttons, the program should verify that the values make sense and protect itself against invalid input such as the user entering the quantity "one," as shown in Figure 21-1.

Note

You can download the code and resources for this Try It from the book's web page at www.wrox.com or www.vb-helper.com/24hourvb.html. You can find the code in the Lesson21 folder.

Figure 21-1

Figure 21.1. Figure 21-1

Lesson Requirements

In this lesson:

  • Copy the program you built in Lesson 19, Exercise 4 (or download Lesson 19's version from the book's web site).

  • The previous version of this program uses repeated code to perform calculations when the user clicks either Calculate or OK. Move that code into a new ValuesAreOk function that validates the user's inputs. In addition to protecting the form from format errors, the function should verify that:

    • Item name is not blank.

    • Price Each > 0.

    • Quantity > 0.

  • If ValuesAreOk finds a problem, it should:

    • Tell the user.

    • Set focus to the textbox that caused the problem.

    • Return False.

  • If ValuesAreOk finds that all of the values are okay, it should return True.

Hints

  • If the user clicks the OK button, the form should close only if the user's inputs are valid. Be sure the OK button's DialogResult property doesn't automatically close the form.

  • Use Try Catch blocks to protect against format errors.

STEP-BY-STEP

  • Copy the program you built in Lesson 19, Exercise 4 (or download Lesson 19's version from the book's web site).

    1. This is straightforward.

  • The previous version of this program uses repeated code to perform calculations when the user clicks either Calculate or OK. Move that code into a new ValuesAreOk function that validates the user's inputs. In addition to protecting the form from format errors, the function should verify that:

    • Item name is not blank.

    • Price Each > 0.

    • Quantity > 0.

    1. Copy the item name into a String variable and verify that its length is at least 1.

    2. Parse Price Each and verify that it is greater than zero.

    3. Parse Quantity and verify that it is greater than zero.

  • If ValuesAreOk finds a problem, it should:

    • Tell the user.

    • Set focus to the textbox that caused the problem.

    • Return False.

    1. Whenever the function finds a problem, it should display a message box, set focus to the textbox containing the error, and return False. For example, the following code shows how you might verify the price each:

      ' Validate price each.
      Try
          PriceEach = Decimal.Parse(
              txtPriceEach.Text,
              System.Globalization.NumberStyles.Any)
      
          If (PriceEach <= 0) Then
              MessageBox.Show("Price Each must be greater than 0.")
              txtPriceEach.Focus()
              Return False
          End If
      Catch ex As Exception
          MessageBox.Show("Price Each must be a dollar amount." &
              vbCrLf & vbCrLf & ex.Message)
          txtPriceEach.Focus()
          Return False
      End Try
  • If ValuesAreOk finds that all of the values are okay, it should return True.

    1. Each of the method's tests returns if there is a problem; therefore, if the code reaches the end of the routine, all the tests passed. At that point, simply return True.

Note

Please select Lesson 21 on the DVD to view the video that accompanies this lesson.

EXERCISES

  1. Copy the LCM program you built in Lesson 20, Exercise 1 (or download Lesson 20's version from the book's web site) and add error handling to it. If a value causes an error, display a message and set focus to its textbox. Hints: Validate both the GCD and LCM methods so they only allow inputs greater than zero. That way they're both covered if a different program uses GCD directly. Also, use a Try Catch block in the Calculate button's Click event handler to protect against format errors.

  2. Copy the Fibonacci program you built in Lesson 19, Exercise 2 (or download Lesson 19's version from the book's web site). Move the calculation itself into a new function that throws an error if its input is less than zero. Add error handling and validation to the main program.

  3. (SimpleEdit) Copy the SimpleEdit program you built in Lesson 20, Exercise 4 (or download Lesson 20's version from the book's web site) and add error handling to the functions that open and save files. To test the program, open the file Test.rtf in Microsoft Word. Make changes in the SimpleEdit program and try to save them into that file.

  4. The quadratic equation finds solutions to equations with the following form, where a, b, and c are constants:

    Equation 21.1. 

    EXERCISES

    The solutions to this equation (the values of x that make it true) are given by the quadratic formula:

    Equation 21.2. 

    EXERCISES

    Build a program similar to the one shown in Figure 21-2 that calculates solutions to quadratic equations. Use a Try Catch block to protect against format errors. Hints: Use Math.Sqrt to take square roots. The equation has zero, one, or two real solutions depending on whether the discriminant b24ac is less than, equal to, or greater than zero. Use If statements to avoid trying to take the square root of a negative number.

Figure 21-2

Figure 21.2. Figure 21-2

Note

You can find solutions to this lesson's exercises in the Lesson21 folder inside the download available on the book's web site at www.wrox.com or www.vb-helper.com/24hourvb.html.

Figure 21-2
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset