Home

Errors and Exception Handling

 

Error Handling

 

Overview

When using your application, a user may encounter various types of problems, some of which could come from you by some negligence when you worked on the project. Some other problems could occur only at the time the application is being used. Some other problems could be caused by the computer on which the application is being used. While developing your application, you should anticipate as many problems as possible as take appropriate actions.

 

Error Categories

There are three main types of errors that could occur while your application is being used:

  1. Syntax Errors: A syntax error comes from your mistyping a word or forming a bad expression in your code. It could be that you misspelled a keyword such as ByVel instead of ByVal. It could also be a bad expression. Examples are:

    524+ + 62.55

    if Number == 2 Number = 6

    If you use the Microsoft Visual Basic IDE to write your code, it would point out the errors while you are writing your code, giving up time to fix them. There are other syntax errors that the IDE may not detect. This could come from using a data type or class that doesn't exist.

    When you compile your application, the compiler can let you know about other syntax errors. Those types of errors are those the compiler cannot execute. It this case, the compiler intercepts, stops, and lets you know. For this reason, syntax errors are almost the easiest to fix. Most of the time, the compiler would point out where the problem is so you can fix it.
  2. Run-Time Errors: After all syntax errors have been fixed, the program may be ready for the user. The time period a person is using an application is called run-time. There are different types of problems that a user may face when interacting with your program. For example, imagine that, in your code, you indicate that a picture would be loaded and displayed to the user but you forget to ship the picture or the directory of the picture indicated in your code becomes different when a user opens your application. In this case, when you compiled and executed the application in your machine, everything was fine. This is a type of run-time error.
    Run-time errors are mostly easy to fix because you will know what the problem is occurring and why.
  3. Logic Errors: These are errors that don't fit in any of the above categories. They could be caused by the user misusing your application, a problem with the computer on which the application is running while the same application is working fine in another computer. Because logic errors can be vague, they can also be difficult to fix.

One of the best qualities of an effective programmer is to anticipate as many problems as possible and to deal with them in the early stages. Some problems can be easy to fix. With some others, you will simply need more experience to know how to fix them. Unfortunately, it will not be unusual to have users asking you to fix your application when a problem may not come from it.

 

Error Handling

 

Introduction 

Most or early errors occur in your code. If you are using the IDE to write your code, it can help you detect syntax errors and fix them. If you are using Notepad and the free vbc compiler as we have done so far, you can start by paying attention to your code. When you think everything is fine, compile your code. If there is a syntax error, the compiler will let you know. If there is no syntax error, the compilation will be over and the executable will be ready. You can then execute the application to see the result. If the user is not asked to provide value(s), you are less likely to get a run-time error.

A run-time error is one that occurs when using your application. Suppose you write the following code:

Imports System

Module Exercise

    Sub Main()
        Dim Number As Double
        Dim Twice As Double

        Console.Write("Enter a number: ")
        Number = Console.ReadLine()
        Twice = Number * 2

        Console.WriteLine("{0} * 2 = {1}", Number, Twice)
        Console.WriteLine()
    End Sub

End Module

Here is an example of executing it:

Enter a number: 246.88
246.88 * 2 = 493.76

The first thing your should do is to imagine what could cause a problem. If you think there is such a possibility, start by creating a label that could be used to transfer code if a problem occurs. Here is an example:

Imports System

Module Exercise

    Sub Main()
        Dim Number As Double
        Dim Twice As Double

        Console.Write("Enter a number: ")
        Number = Console.ReadLine()
        Twice = Number * 2

        Console.WriteLine("{0} * 2 = {1}", Number, Twice)
        Console.WriteLine()

ThereWasAProblem:
        Console.WriteLine("There was a problem when executing your instructions")

    End Sub

End Module

As we saw when we introduced labels, if you create one, you should tell the compiler when to jump to that label. Otherwise, as in this case, the label section would always execute. Here is an example of running the above version:

Enter a number: 12.46
12.46 * 2 = 24.92

There was a problem when executing your instructions

In this case, we want the label section to execute only when we want it to. To prevent the compiler from reaching this section if not directed so, you can add an Exit Sub line above the label section:

Imports System

Module Exercise

    Sub Main()
        Dim Number As Double
        Dim Twice As Double

        Console.Write("Enter a number: ")
        Number = Console.ReadLine()
        Twice = Number * 2

        Console.WriteLine("{0} * 2 = {1}", Number, Twice)
        Console.WriteLine()

        Exit Sub

ThereWasAProblem:
        Console.WriteLine("There was a problem when executing your instructions")

    End Sub

End Module

This time if you execute the program with an appropriate value, the label section would not be reached.

In Case Of Error, Jump To Label

The above program will compile fine. When executing it, imagine that the user types an inappropriate value such as 24$.58 instead of 244.58. In this case, the value is not a number and the program would "crash" and let you know that there was a problem. If you have some experience, you would know what the problem was, otherwise, you would face a vague explanation. The short story is that the compiler couldn't continue because, in this case, it could not multiply 24$.58 by 2.

If a problem occurs when a person is using your program, the compiler may display a nasty and insignificant message to the user who would not know what to do with it. Therefore, you can start by creating an appropriate label as introduced above. An error normally occurs in a procedure. Therefore, to make your code easier to read, you should create a label that shows that it is made for an error instead of being a regular label. The label should also reflect the name of the procedure. Here is an example:

Imports System

Module Exercise

    Sub Main()
        Dim Number As Double
        Dim Twice As Double

        Console.Write("Enter a number: ")
        Number = Console.ReadLine()
        Twice = Number * 2

        Console.WriteLine("{0} * 2 = {1}", Number, Twice)
        Console.WriteLine()

        Exit Sub

Err_Main:
        Console.WriteLine("In Main(): The operation could not be executed")

    End Sub

End Module

When you think there will be a problem in your code, somewhere in the lines under the name of the procedure but before the line that could cause the problem, type On Error GoTo followed by the name of the label that would deal with the error. Here is an example:

Imports System

Module Exercise

    Sub Main()
        On Error GoTo Err_Main
        Dim Number As Double
        Dim Twice As Double

        Console.Write("Enter a number: ")
        Number = Console.ReadLine()
        Twice = Number * 2

        Console.WriteLine("{0} * 2 = {1}", Number, Twice)
        Console.WriteLine()

        Exit Sub

Err_Main:
        Console.WriteLine("In Main(): The operation could not be executed")

    End Sub

End Module

Here is an example of running the program:

Enter a number: 24$.58
In Main(): The operation could not be executed

When the On Error GoTo statement is used, this indicates to the compiler that if any type of error occurs while the code of this procedure is executed, transfer the compilation to the label. In this case, as soon as something bad happens, the compiler marks the area where the problem occurred, skips the normal code and jumps to the label indicated by the On Error GoTo line. After the section of that label is executed, the compiler returns where the error occurred. If there is nothing to solve the problem, the compiler continues down but without executing the lines of code involved. In this case, it would encounter the Exit Sub line and get out of the procedure.

In Case Of Error, Jump To Line #

Although the label is more explicit, it only indicates to the compiler what line to jump to in case of a problem. The alternative is to specify a line number instead of a label.

Resume 

If a problem occurs in your code and you provide a label to display a friendly message as done above, the compiler would display the message and exit from the procedure. If this happens, as mentioned above, when the compiler returns where the problem occurred, you can provide an alternate. For example, in our program, if the user provides an inappropriate value that causes the error, you can provide an alternate value and ask the compiler to continue as if nothing happened. In this case, you want to compiler to "resume" its activity.

To indicate that the program should continue, you can use the Resume keyword. Here is an example:

Imports System

Module Exercise

    Sub Main()
        On Error GoTo Err_Main
        Dim Number As Double
        Dim Twice As Double

        Console.Write("Enter a number: ")
        Number = Console.ReadLine()

	Resume

        Twice = Number * 2

        Console.WriteLine("{0} * 2 = {1}", Number, Twice)
        Console.WriteLine()

        Exit Sub

Err_Main:
        Console.WriteLine("In Main(): The operation could not be executed")

    End Sub

End Module

When an error occurs, if you want the program to continue with with an alternate value than the one that caused the problem, in the label section, type Resume Next. Here is an example:

Imports System

Module Exercise

    Sub Main()
        On Error GoTo Err_Main
        Dim Number As Double
        Dim Twice As Double

        Console.Write("Enter a number: ")
        Number = Console.ReadLine()

	Resume

        Twice = Number * 2

        Console.WriteLine("{0} * 2 = {1}", Number, Twice)
        Console.WriteLine()

        Exit Sub

Err_Main:
        Console.WriteLine("In Main(): The operation could not be executed")
	Resume Next

    End Sub

End Module

In this case, since any numeric variable is initialized with 0, when the compiler returns to the line of code that caused the problem, it would use 0 as a substitute to the inappropriate value. Here is an example of running the above program:

Enter a number: 24$.58
In Main(): The operation could not be executed
0 * 2 = 0

Based on this, you can provide a new value to use in case of error. Here is an example:

Imports System

Module Exercise

    Sub Main()
        On Error GoTo Err_Main
        Dim Number As Double
        Dim Twice As Double

        Console.Write("Enter a number: ")
        Number = Console.ReadLine()

        Twice = Number * 2

        Console.WriteLine("{0} * 2 = {1}", Number, Twice)
        Console.WriteLine()

        Exit Sub

Err_Main:
        Console.WriteLine("Invalid Number: The value you provided is inappropriate")
        Console.WriteLine("10 will be used instead")
        Number = 10
        Resume Next
    End Sub

End Module

Here is one example of running the program:

Enter a number: 244.58
244.58 * 2 = 489.16

Here is another example of running the same program:

Enter a number: 24$.58
Invalid Number: The value you provided is inappropriate
10 will be used instead
10 * 2 = 20
 

The Err Object

To support error handling, VBasic provides a global variable named Err. This allows you to identify the error and its description. Because an error depends on what caused it and why, the values of the Err variable also depend and are not always the same.

 

Exception Handling

 

Introduction

As opposed to the traditional techniques used to deal with errors, VBasic now supports a technique referred to as exception handling. This technique was mostly used by other languages such as C/C++, Object Pascal, C#, etc. This technique is also referred to as structured exception handling (SEH). You should abandon the above On Error traditional error handling and all of your new code. In fact the above On Error techniques of dealing with errors is referred to as unstructured exception handling. Because both techniques are inherently different, you cannot use both in the same procedure.

 

Practical LearningPractical Learning: Introducing Exception Handling

  1. Start Notepad and type the following:
     
    Public Structure OrderInformation
        ' Basic information about an order
        Public CustomerName As String
        Public HomePhone As String
        Public OrderDate As DateTime
        Public OrderTime As DateTime
        Public NumberOfShirts As Integer
        Public NumberOfPants As Integer
        Public NumberOfDresses As Integer
    End Structure
  2. Save the file in a new folder named GCS4
  3. Save the file itself as OrderInfo.vb
  4. To start a new file, on the main menu of Notepad, click File -> New and type the following:
     
    Public Class OrderProcess
        ' Price of items
        ReadOnly PriceOneShirt As Double = 0.95
        ReadOnly PriceAPairOfPants As Double = 2.95
        ReadOnly PriceOneDress As Double = 4.55
        ReadOnly TaxRate As Double = 0.0575            ' 5.75%
    
        Dim order As OrderInformation
    
        ' Each of these sub totals will be used for cleaning items
        Private SubTotalShirts As Double
        Private SubTotalPants As Double
        Private SubTotalDresses As Double
    
        ' Values used to process an order
        Private TotalOrder As Double
        Private TaxAmount As Double
        Private SalesTotal As Double
        Private AmountTended As Double
        Private Difference As Double
    
        Public Sub ProcessOrder()
            Console.WriteLine("-/- Georgetown Cleaning Services -/-")
            ' Request order information from the user
            Console.Write("Enter Customer Name:  ")
            Order.CustomerName = Console.ReadLine()
            Console.Write("Enter Customer Phone: ")
            Order.HomePhone = Console.ReadLine()
            Console.Write("Enter the order date(mm/dd/yyyy):  ")
            Order.OrderDate = DateTime.Parse(Console.ReadLine())
            Console.Write("Enter the order time(hh:mm AM/PM): ")
            Order.OrderTime = DateTime.Parse(Console.ReadLine())
    
            ' Request the quantity of each category of items
            Console.Write("Number of Shirts:  ")
            order.NumberOfShirts = CInt(Console.ReadLine())
    
            Console.Write("Number of Pants:   ")
            order.NumberOfPants = CInt(Console.ReadLine())
    
            Console.Write("Number of Dresses: ")
            order.NumberOfDresses = CInt(Console.ReadLine())
    
            ' Perform the necessary calculations
            SubTotalShirts = Order.NumberOfShirts * PriceOneShirt
            SubTotalPants = Order.NumberOfPants * PriceAPairOfPants
            SubTotalDresses = Order.NumberOfDresses * PriceOneDress
            ' Calculate the "temporary" total of the order
            TotalOrder = SubTotalShirts + SubTotalPants + SubTotalDresses
    
            ' Calculate the tax amount using a constant rate
            TaxAmount = TotalOrder * TaxRate
            ' Add the tax amount to the total order
            SalesTotal = TotalOrder + TaxAmount
    
            ' Communicate the total to the user...
            Console.WriteLine(vbcrlf & "The Total order is: {0:C}", SalesTotal)
            ' and request money for the order
            Console.Write("Amount Tended? ")
            AmountTended = Decimal.Parse(Console.ReadLine())
    
            ' Calculate the difference owed to the customer
            ' or that the customer still owes to the store
            Difference = AmountTended - SalesTotal
    
            ShowReceipt()
        End Sub
    
        Private Sub ShowReceipt()
            Console.WriteLine()
            ' Display the receipt
            Console.WriteLine("====================================")
            Console.WriteLine("-/- Georgetown Cleaning Services -/-")
            Console.WriteLine("====================================")
            Console.WriteLine("Customer:    {0}", Order.CustomerName)
            Console.WriteLine("Home Phone:  {0}", Order.HomePhone)
            Console.WriteLine("Order Date:  {0:D}", Order.OrderDate)
            Console.WriteLine("Order Time:  {0:t}", Order.OrderTime)
            Console.WriteLine("------------------------------------")
            Console.WriteLine("Item Type  Qty Unit/Price Sub-Total")
            Console.WriteLine("------------------------------------")
            Console.WriteLine("Shirts     {0,3}   {1,4}      {2,6}", _
        order.NumberOfShirts, PriceOneShirt, SubTotalShirts)
            Console.WriteLine("Pants      {0,3}   {1,4}      {2,6}", _
             order.NumberOfPants, PriceAPairOfPants, SubTotalPants)
            Console.WriteLine("Dresses    {0,3}   {1,4}      {2,6}", _
        order.NumberOfDresses, PriceOneDress, SubTotalDresses)
            Console.WriteLine("------------------------------------")
            Console.WriteLine("Total Order:   {0,6}", TotalOrder.ToString("C"))
            Console.WriteLine("Tax Rate:      {0,6}", TaxRate.ToString("P"))
            Console.WriteLine("Tax Amount:    {0,6}", TaxAmount.ToString("C"))
            Console.WriteLine("Net Price:     {0,6}", SalesTotal.ToString("C"))
            Console.WriteLine("------------------------------------")
            Console.WriteLine("Amount Tended: {0,6}", AmountTended.ToString("C"))
            Console.WriteLine("Difference:    {0,6}", Difference.ToString("C"))
            Console.WriteLine("====================================")
        End Sub
    End Class
  5. Save the file as OrderProcess.vb in the GCS4 folder
  6. To start a new file, on the main menu of Notepad, click File -> New and type the following:
     
    Public Class Exercise
    
        Public Shared Sub main()
            Dim Order As OrderProcessing = New OrderProcessing
    
            Order.ProcessOrder()
        End Sub
    End Class
  7. Save the file as Exercise.vb in the GCS4 folder
  8. Open the Command Prompt and change to the above GCS4 directory
  9. To create the application, type
    vbc /out:"Georgetown Cleaning Services".exe OrderInfo.vb OrderProcess.cs Exercise.vb and press Enter
  10. To execute the application, type "Georgetown Cleaning Services" and press Enter. Here is an example:
     
    -/- Georgetown Cleaning Services -/-
    Enter Customer Name:  Perl
    Enter Customer Phone: 301-884-0912
    Enter the order date(mm/dd/yyyy):  08/04/0
    Enter the order time(hh:mm AM/PM): 10:26
    Number of Shirts:  12
    Number of Pants:   2
    Number of Dresses: 8
    
    The Total order is: $56.79
    Amount Tended? 70
    
    ====================================
    -/- Georgetown Cleaning Services -/-
    ====================================
    Customer:    Perl
    Home Phone:  301-884-0912
    Order Date:  Monday, August 04, 2003
    Order Time:  10:26 AM
    ------------------------------------
    Item Type  Qty Unit/Price Sub-Total
    ------------------------------------
    Shirts      12   0.95       11.40
    Pants        2   2.95        5.90
    Dresses      8   4.55       36.40
    ------------------------------------
    Total Order:   $53.70
    Tax Rate:      5.75 %
    Tax Amount:     $3.09
    Net Price:     $56.79
    ------------------------------------
    Amount Tended: $70.00
    Difference:    $13.21
    ====================================
  11. Return to Notepad

Try to Catch the Error

As mentioned already, errors are likely going to occur in your program. The more you anticipate them and take action, the better your application will be. We have already seen that syntax errors are usually human mistakes such as misspelling, bad formulation of expressions, etc. The compiler will usually help you fix the problem by pointing it out.

SEH is based on two main keywords: Try and Catch. An exception handling section starts with the Try keyword and stops with the End Try statement. Between Try and End Try, there must by at least one Catch section. Therefore, exception handling uses the following formula:

Try

Catch
        
End Try

Exception handling always starts with the Try keyword. Under the Try line, write the normal code that the compiler must execute. Here is an example:

Imports System

Module Exercise

    Sub Main()
        Dim Number As Double
        Dim Twice As Double
        Try
            Console.Write("Enter a number: ")
            Number = Console.ReadLine()

            Twice = Number * 2

            Console.WriteLine("{0} * 2 = {1}", Number, Twice)
            Console.WriteLine()
            
        End Try

    End Sub

End Module

As the compiler is treating code in the Try section, if it encounters a problem, it "gets out" of the Try section and starts looking for a Catch section. Therefore, you must always have a Catch section. If you don't, as seen on the above code, the program will not compile. A Catch section must be written before the End Try line:

Imports System

Module Exercise

    Sub Main()
        Dim Number As Double
        Dim Twice As Double
        Try
            Console.Write("Enter a number: ")
            Number = Console.ReadLine()

            Twice = Number * 2

            Console.WriteLine("{0} * 2 = {1}", Number, Twice)
            Console.WriteLine()

        Catch

        End Try

    End Sub

End Module

When the Catch keyword is simply written as above, it would be asked to treat any error that occurs. For example, here is one example of executing the above program:

Enter a number: 244.58
244.58 * 2 = 489.16

Here is another example of executing the same program:

Enter a number: 24$.58

Notice that the program stops if there is any type of problem but in this case, it doesn't bother to let the user know why there is no result displayed. Because there can be various types of errors in a program, you also should make your program more intuitive and friendlier so that, when a problem occurs, the user would know the type of problem. This is also useful if somebody calls you and says that your program is not functioning right. If there is a way the user can tell what exact type of error is displaying, may be you would find the solution faster.

Practical LearningPractical Learning: Trying Exceptions

  1. To introduce exceptions, access the OrderProcess.vb file and change it as follows:
     
    Public Class OrderProcessing
        
        . . . No Change
    
        Public Sub ProcessOrder()
            Console.WriteLine("-/- Georgetown Cleaning Services -/-")
            ' Request order information from the user
            
            . . . No Change
    
            ' Request the quantity of each category of items
            Try
                Console.Write("Number of Shirts:  ")
                order.NumberOfShirts = CInt(Console.ReadLine())
            Catch
    
            End Try
    
            Try
                Console.Write("Number of Pants:   ")
                order.NumberOfPants = CInt(Console.ReadLine())
            Catch
    
            End Try
    
            Try
                Console.Write("Number of Dresses: ")
                order.NumberOfDresses = CInt(Console.ReadLine())
            Catch
    
            End Try
    
            . . . No Change
    
        End Sub
    
        Private Sub ShowReceipt()
            
            . . . No Change
    
        End Sub
    End Class
  2. Save the file

Exceptions and Custom Messages

As mentioned already, if an error occurs when processing the program in the try section, the compiler transfer the processing to the next catch section. You can then use the catch section to deal with the error. At a minimum, you can display a message to inform the user. Here is an example:

Imports System

Public Class Exercise
    Public Shared Sub main()
        Dim Number As Double

        Try
            Console.Write("Type a number: ")
            Number = cdbl(Console.ReadLine())

            Console.WriteLine(vbcrlf & "{0} * 2 = {1}", Number, Number * 2)
        Catch
            Console.WriteLine("There was a problem with the program")
        End Try
    End Sub
End Class

Of course, this type of message is not particularly clear but this time, the program will not crash. In the next sections, we will learn better ways of dealing with the errors and the messages.

 

Practical LearningPractical Learning: Displaying Custom Messages

  1. To display custom messages to the user, change the OrderProcess.vb file as follows:
     
    Public Class OrderProcessing
    
        . . . No Change
    
        Public Sub ProcessOrder()
            
            . . . No Change
    
            ' Request the quantity of each category of items
            Try
                Console.Write("Number of Shirts:  ")
                order.NumberOfShirts = CInt(Console.ReadLine())
            Catch
                Console.WriteLine("The value you typed for the number of " & _
                      "shirts is not a valid number")
            End Try
    
            Try
                Console.Write("Number of Pants:   ")
                order.NumberOfPants = CInt(Console.ReadLine())
            Catch
                Console.WriteLine("The value you typed for the number of " & _
                              "pair or pants is not a valid number")
            End Try
    
            Try
                Console.Write("Number of Dresses: ")
                order.NumberOfDresses = CInt(Console.ReadLine())
            Catch
                Console.WriteLine("The value you typed for the number of " & _
                              "dresses is not a valid number")
            End Try
    
            ' Perform the necessary calculations
            SubTotalShirts = order.NumberOfShirts * PriceOneShirt
            SubTotalPants = order.NumberOfPants * PriceAPairOfPants
            SubTotalDresses = order.NumberOfDresses * PriceOneDress
            ' Calculate the "temporary" total of the order
            TotalOrder = SubTotalShirts + SubTotalPants + SubTotalDresses
    
            ' Calculate the tax amount using a constant rate
            TaxAmount = TotalOrder * TaxRate
            ' Add the tax amount to the total order
            SalesTotal = TotalOrder + TaxAmount
    
            ' Communicate the total to the user...
            Console.WriteLine(vbCrLf & "The Total order is: {0:C}", SalesTotal)
            ' and request money for the order
            Try
                Console.Write("Amount Tended? ")
                AmountTended = CDbl(Console.ReadLine())
            Catch
                Console.WriteLine("You were asked to enter an amount of money but...")
            End Try
    
            ' Calculate the difference owed to the customer
            ' or that the customer still owes to the store
            Difference = AmountTended - SalesTotal
    
            ShowReceipt()
        End Sub
    
        Private Sub ShowReceipt()
            . . . No Change
        End Sub
    End Class
  2. Save the file
  3. At the Command Prompt, to compile the application, type
    vbc /out:"Georgetown Cleaning Services".exe OrderInfo.vb OrderProcess.cs Exercise.vb and press Enter
  4. To execute the application, type "Georgetown Cleaning Services" and press Enter. Here is an example:
     
    -/- Georgetown Cleaning Services -/-
    Enter Customer Name:  Alexandria
    Enter Customer Phone: (102) 797-8382
    Enter the order date(mm/dd/yyyy):  04/02/2001
    Enter the order time(hh:mm AM/PM): 09:22 AM
    Number of Shirts:  6
    Number of Pants:   W
    The value you typed for the number of pair or pants is not a valid number
    Number of Dresses: 5
    
    The Total order is: $30.09
    Amount Tended? _100D
    You were asked to enter an amount of money but...
    
    ====================================
    -/- Georgetown Cleaning Services -/-
    ====================================
    Customer:    Alexandria
    Home Phone:  (102) 797-8382
    Order Date:  Monday, April 02, 2001
    Order Time:  9:22 AM
    ------------------------------------
    Item Type  Qty Unit/Price Sub-Total
    ------------------------------------
    Shirts       6   0.95        5.70
    Pants        0   2.95           0
    Dresses      5   4.55       22.75
    ------------------------------------
    Total Order:   $28.45
    Tax Rate:      5.75 %
    Tax Amount:     $1.64
    Net Price:     $30.09
    ------------------------------------
    Amount Tended:  $0.00
    Difference:    ($30.09)
    ====================================
  5. Return to Notepad

Exceptions in the .NET Framework

 

The Exception Class

The .NET Framework provides a very high level of support for exception handling, and VBasic highly takes advantage of this. To support exception handling, the .NET Framework provides the Exception class. Once the compiler encounters an error, the Exception class allows you to identify the type of error and take appropriate action(s).

Normally, Exception mostly serves as the general class of exceptions. Anticipating various types of problems that can occur in a program, Microsoft derived various classes from Exception to make this issue friendlier. As a result, almost any type of exception you may encounter already has a class created to deal with it. Therefore, when your program faces an exception, you can easily access the class appropriate for that error. There are so many exception classes that we cannot study or review them all. The solution we will use is to introduce or review a class when we meet its type of error.

The Exception's Message

In exception handling, errors are dealt with in the Catch section. On the right side of Catch, behave as if you were declaring a variable of the type of exception you want to deal with. By default, an exception is first of type Exception. Based on this, a typical formula to implement exception handling is:

Try
	' Process the normal flow of the program here
Catch exc As Exception
	' Deal with the exception here
End Try

When an exception occurs in the Try section, code compilation is transferred to the Catch section. If you declare the exception as an Exception type, this class will identify the error. One of the properties of the Exception class is called Message. This property contains a string that describes the type of error that occurred. You can then use this Exception.Message property to display an error message if you want. Here is an example:

Imports System

Public Class Exercise
    Public Shared Sub main()
        Dim Number As Double

        Try
            Console.Write("Type a number: ")
            Number = CDbl(Console.ReadLine())

            Console.WriteLine(vbcrlf & "{0} * 2 = {1}", Number, Number * 2)
        Catch ex As Exception
            Console.WriteLine(ex.Message)
        End Try
    End Sub
End Class

Here is an example of running the program:

Type a number: 45KP2
Cast from string "45KP2" to type 'Double' is not valid.
 

Custom Error Messages

As you can see, one of the strengths of the Exception.Message property is that it gives you a good indication of the type of problem that occurred. Sometimes, the message provided by the Exception class may not appear explicit enough. In fact, you may not want to show it to the user since, as in this case, the user may not understand what the word "Cast" in this context means and why it is being used. As an alternative, you can create your own message and display it to the user. Here is an example:

Imports System

Public Class Exercise
    Public Shared Sub main()
        Dim Number As Double

        Try
            Console.Write("Type a number: ")
            Number = CDbl(Console.ReadLine())

            Console.WriteLine(vbcrlf & "{0} * 2 = {1}", Number, Number * 2)
        Catch ex As Exception
            Console.WriteLine("The operation could not be carried because " & _
                 "the number you typed is not valid")
        End Try
    End Sub
End Class

Here is an example of running the program:

Type a number: 88D.46
The operation could not be carried because the number you typed is not valid

You can also combine the Exception.Message message and your own message:

Imports System

Public Class Exercise
    Public Shared Sub main()
        Dim Number As Double

        Try
            Console.Write("Type a number: ")
            Number = CDbl(Console.ReadLine())

            Console.WriteLine(vbcrlf & "{0} * 2 = {1}", Number, Number * 2)
        Catch ex As Exception
            Console.WriteLine(ex.Message & vbCrLf & _
                              "The operation could not be carried because " & _
                              "the number you typed is not valid")
        End Try
    End Sub
End Class

Here is an example of running the program:

Type a number: 45PK12
Cast from string "45PK12" to type 'Double' is not valid.
The operation could not be carried because the number you typed is not valid

A Review of .NET Exception Classes

 

Introduction

The .NET Framework provides various classes to handle almost any type of exception you can think of. There are so many of these classes that we can only mention the few that we regularly use in our application.

There are two main ways you can use one of the classes of the .NET Framework. If you know for sure that a particular exception will be produced, pass its name to the Catch clause and display a custom message. The second option you have consists of using the throw keyword. We will study it later.

From now on, we will try to always indicate the type of exception that could be thrown if something goes wrong in a program

InvalidCastException

When studying data formatting in Lesson 5, we saw that everything the user types into an application using the keyboard is primarily a string and that you must convert it to the appropriate type before using it. When you request a specific type of value from the user, after the user has typed it and you decide to convert it to the appropriate type using one of the built-in conversion functions (CDbl(), CInt, CDate, CSng, CDec, etc), if your conversion fails, the program produces (in the next lessons, we will use he word "throw") an error. The error is from the InvalidCastException class.

Here is a program that deals with a InvalidCastException exception:

Imports System

Public Class Exercise
    Public Shared Sub main()
        Dim Number As Double

        Try
            Console.Write("Type a number: ")
            Number = CDbl(Console.ReadLine())

            Console.WriteLine("\n{0} * 2 = {1}", Number, Number * 2)
        Catch ex As InvalidCastException
            Console.WriteLine("You typed an invalid number")
        End Try
    End Sub
End Class

Here is an example of running the program:

Type a number: 39W.68g
You typed an invalid number
 

Practical LearningPractical Learning: Using the InvalidCastException Class

  1. Change the OrderProcess.vb file as follows:
     
    Public Class OrderProcessing
        
        . . . No Change
    
        Public Sub ProcessOrder()
            
            . . . No Change
    
            ' Request the quantity of each category of items
            Try
                Console.Write("Number of Shirts:  ")
                order.NumberOfShirts = CInt(Console.ReadLine())
            Catch exc As InvalidCastException
                Console.WriteLine("The value you typed for the number of " & _
                      "shirts is not a valid number")
            End Try
    
            Try
                Console.Write("Number of Pants:   ")
                order.NumberOfPants = CInt(Console.ReadLine())
            Catch exc As InvalidCastException
                Console.WriteLine("The value you typed for the number of " & _
                              "pair or pants is not a valid number")
            End Try
    
            Try
                Console.Write("Number of Dresses: ")
                order.NumberOfDresses = CInt(Console.ReadLine())
            Catch exc As InvalidCastException
                Console.WriteLine("The value you typed for the number of " & _
                              "dresses is not a valid number")
            End Try
    
            ' Perform the necessary calculations
            SubTotalShirts = order.NumberOfShirts * PriceOneShirt
            SubTotalPants = order.NumberOfPants * PriceAPairOfPants
            SubTotalDresses = order.NumberOfDresses * PriceOneDress
            ' Calculate the "temporary" total of the order
            TotalOrder = SubTotalShirts + SubTotalPants + SubTotalDresses
    
            ' Calculate the tax amount using a constant rate
            TaxAmount = TotalOrder * TaxRate
            ' Add the tax amount to the total order
            SalesTotal = TotalOrder + TaxAmount
    
            ' Communicate the total to the user...
            Console.WriteLine(vbCrLf & "The Total order is: {0:C}", SalesTotal)
            ' and request money for the order
            Try
                Console.Write("Amount Tended? ")
                AmountTended = CDbl(Console.ReadLine())
            Catch exc As InvalidCastException
                Console.WriteLine("You were asked to enter an amount of money but...")
            End Try
    
            ' Calculate the difference owed to the customer
            ' or that the customer still owes to the store
            Difference = AmountTended - SalesTotal
    
            ShowReceipt()
        End Sub
    
        Private Sub ShowReceipt()
            
            . . . No Change
            
        End Sub
    End Class
  2. Save the file
  3. At the Command Prompt, to compile the application, type
    vbc /out:"Georgetown Cleaning Services".exe OrderInfo.vb OrderProcess.cs Exercise.vb and press Enter
  4. To execute the application, type "Georgetown Cleaning Services" and press Enter
  5. Return to Notepad

The OverflowException Exception

A computer application receives, processes, and produces values on a regular basis as the program is running. To better manage these values, as we saw when studying variables and data types in Lesson 2, the compiler uses appropriate amounts of space to store its values. It is not unusual that either you the programmer or a user of your application provide a value that is beyond the allowed range of the data type. For example, a byte uses 8 bits to store a value and a combination of 8 bits can store a number no more than 255. If you provide a value higher than 255 to be stored in a byte, you get an error. Consider the following program:

Imports System

Public Class Exercise
    Public Shared Sub main()
        Dim NumberOfPages As Byte

        Console.Write("Enter the number of pages of the newspaper: ")
        NumberOfPages = CByte(Console.ReadLine())

        Console.WriteLine("Number of Pages of the Newspaper: {0}", NumberOfPages)
    End Sub
End Class

When a value beyond the allowable range is asked to be stored in memory, the compiler produces (or "throws") an error of the OverflowException class. Here is an example of running the program:

Enter the number of pages of the newspaper: 462

Unhandled Exception: System.OverflowException: Arithmetic operation resulted in
an overflow.
   at Microsoft.VisualBasic.CompilerServices.ByteType.FromString(String Value)
   at Project5.Exercise.main() in C:\Programs\MSVB .NET 2003\Project5\Exercise.v
b:line 8

As with the other errors, when this exception is thrown, you should take appropriate action.

FormatException

Once again, when studying the techniques of converting or formatting values in Lesson 5, we saw that a value is passed to a conversion function for analysis. For a primitive data type, the conversion function scans the string and if the string cannot be converted into a valid character or number, the compiler usually throws an InvalidCastException exception as we saw above. Other data types such as Date also use use this technique to scan the value submitted to it. For example, if you request a date value from the user, the CDate() function scans the string to validate it. In US English, CDate() expects the user to type a string in the form m/d/yy or mm/dd/yy or mm/dd/yyyy or yyyy/mm/dd. Consider the following program:

Imports System

Public Class Exercise
    Public Shared Sub main()
        Dim DateHired As Date

        Console.Write("Enter Date Hired: ")
        DateHired = DateTime.Parse(Console.ReadLine())

        Console.WriteLine("Date Hired: {0:d}", DateHired)
    End Sub
End Class

If the user types a value that cannot be converted into a valid date, the compiler throws an FormatException exception. Here is an example of running the above program:

Enter Date Hired: 04/12/2002
Date Hired: 4/12/2002

Here is another example of running the program:

Enter Date Hired: 1998/10/24
Date Hired: 10/24/1998

Here is one more example of running the program:

Enter Date Hired: 16/04/2002

Unhandled Exception: System.FormatException: String was not recognized as a vali
d DateTime.
   at System.DateTimeParse.GetDayOfNNY(DateTimeResult result, DateTimeRawInfo ra
w, DateTimeFormatInfo dtfi)
   at System.DateTimeParse.ProcessTerminaltState(Int32 dps, DateTimeResult resul
t, DateTimeRawInfo raw, DateTimeFormatInfo dtfi)
   at System.DateTimeParse.Parse(String s, DateTimeFormatInfo dtfi, DateTimeStyl
es styles)
   at System.DateTime.Parse(String s, IFormatProvider provider, DateTimeStyles s
tyles)
   at System.DateTime.Parse(String s, IFormatProvider provider)
   at System.DateTime.Parse(String s)
   at Project5.Exercise.main() in C:\Programs\MSVB .NET 2003\Project5\Exercise.v
b:line 8

One way you can avoid this is to guide the user about the type of expected value. Here is an example:

Imports System

Public Class Exercise
    Public Shared Sub main()
        Dim DateHired As Date

        Console.Write("Enter Date Hired (mm/dd/yyyy): ")
        DateHired = DateTime.Parse(Console.ReadLine())

        Console.WriteLine("Date Hired: {0:d}", DateHired)
    End Sub
End Class

You should still prepare to take appropriate actions, just in case this error is thrown.

The DivideByZeroException Exception

Division by zero is an operation to always avoid. It is so important that it is one of the most fundamental exceptions of the computer. It is addressed at the core level even by the Intel and AMD processors. It is also addressed by the operating systems at their level. It is also addressed by most, if not all, compilers. It is also addressed by most, if not, all libraries. This means that this exception is never welcomed anywhere. The .NET Framework also provides it own class to face this operation.

If an attempt to divide a value by 0, the compiler throws a DivideByZeroException exception. We will see an example later.

Techniques of Using Exceptions

 

Throwing an Exception

As mentioned above, the Exception class is equipped with a Message property that carries a message for the error that occurred. We also mentioned that the message of this property may not be particularly useful to a user. Fortunately, you can create your own message and pass it to the Exception. To be able to receive custom messages, the Exception class provides the following constructor:

public Exception(string message)

To use it, in the section that you are anticipating the error, type the throw keyword followed by a new instance of the Exception class using the constructor that takes a string. Here is an example:

Imports System

Public Class Exercise
    Public Shared Sub main()
        Dim Number1 As Double, Number2 As Double
        Dim Operator As String
        Dim Result As Double

        Try
            Console.Write("Enter First Number:  ")
            Number1 = CDbl(Console.ReadLine())
            Console.Write("Enter Operation (+, -, *, or /): ")
            Operator = CStr(Console.ReadLine())
            Console.Write("Enter Second Number: ")
            Number2 = CDbl(Console.ReadLine())

            If Operator = "+" Then
                Result = Number1 + Number2
            ElseIf Operator = "-" Then
                Result = Number1 - Number2
            ElseIf Operator = "*" Then
                Result = Number1 * Number2
            ElseIf Operator = "/" Then
                Result = Number1 / Number2
            Else
                Result = 0
            End If

            Console.WriteLine(vbCrLf & "{0} {1} {2} = {3}", _
                              Number1, Operator, Number2, Result)
        Catch ex as Exception
            Console.WriteLine("Somewhere went wrong: your operation could not be performed")
        End Try
    End Sub
End Class

Here is an example of running the program:

Enter First Number:  244.98
Enter Operation (+, -, *, or /): *
Enter Second Number: 24.52

244.98 * 24.52 = 6006.9096

Here is another example of running the program:

Enter First Number:  405.66
Enter Operation (+, -, *, or /): -
Enter Second Number: 24GD
Something went wrong: your operation could not be performed

Here is one more example of running the program:

Enter First Number:  248.12
Enter Operation (+, -, *, or /): $
Enter Second Number: 5.15

248.12 $ 5.15 = 0

Catching Various Exceptions

In the examples above, when we anticipated some type of problem, we instructed the compiler to use our default Catch section. We left it up to the compiler to find out when there was a problem and we provided a Catch section to deal with it. A method with numerous or complex operations and requests can also produce different types of errors. With such a type of program, you should be able to face different problems and deal with them individually, each by its own kind. To do this, you can create different Catch sections, each made for a particular error. The formula used would be:

Try
	' Code to Try
Catch One Type of Exception
	' One Exception
Catch Another Type Of Exception
	' Another Exception
End Try

The compiler would proceed in a top-down:

  1. Following the normal flow of the program, the compiler enters the Try block
  2. If no exception occurs in the Try block, the rest of the Try block is executed
    If an exception occurs in the Try block, the compiler registers the type of error that occurred. If there is a throw line, the compiler registers it also:
    1. The compiler gets out of the Try section
    2. The compiler examines the first Catch. If the first Catch matches the thrown error, that catch executes and the exception handling routine may seize. If the first Catch doesn’t match the thrown error, the compiler proceeds with the next Catch
    3. The compiler checks the next match, if any and proceeds as in the first match. This continues until the compiler finds a Catch that matches the thrown error
    4. If one of the catches matches the thrown error, its body executes. If no Catch matches the thrown error, the compiler calls the Exception class and uses the default message

Multiple catches are written if or when a try block is expected to throw different types of errors.

This program works fine as long as the user types a valid sequence of values made of a number, a valid arithmetic operator, and a number. Anything else, such an invalid number, an unexpected operator, or a wrong sequence (such as a number then another number instead of an operator), would cause an error to be thrown. Obviously various bad things could happen when this program is running. To handle the exceptions that this program could produce, you can start with the most likely problem that would occur. Trusting that a user is able to provide the two numbers that are requested, it is possible that a user would type an invalid operator. For example, for this program we will perform only the addition (+), the subtraction(-), the multiplication(*), and the division(/). Therefore, we will first validate the operator. This can be done as follows:

Imports System

Public Class Exercise
    Public Shared Sub main()
        Dim Number1 As Double, Number2 As Double
        Dim Operator As String
        Dim Result As Double

        Try
            Console.Write("Enter First Number:  ")
            Number1 = CDbl(Console.ReadLine())
            Console.Write("Enter Operation (+, -, *, or /): ")
            Operator = CStr(Console.ReadLine())
            Console.Write("Enter Second Number: ")
            Number2 = CDbl(Console.ReadLine())

            If Operator <> "+" And Operator <> "-" And _
                        Operator <> "*" And Operator <> "/" Then
                Throw New Exception(Operator)
            End If

            If Operator = "+" Then
                Result = Number1 + Number2
            ElseIf Operator = "-" Then
                Result = Number1 - Number2
            ElseIf Operator = "*" Then
                Result = Number1 * Number2
            ElseIf Operator = "/" Then
                Result = Number1 / Number2
            Else
                Result = 0
            End If

            Console.WriteLine(vbCrLf & "{0} {1} {2} = {3}", _
                              Number1, Operator, Number2, Result)
        Catch ex As Exception
            Console.WriteLine(vbCrLf & "Invalid Operator: {0}", Operator)
            Console.WriteLine("Your operation could not be performed")
        End Try
    End Sub
End Class

When this program runs, if the user provides a invalid operator, we register the operator by calling the Exception(string message) constructor and pass it the wrong operator. If this error occurs, the compiler gets out of the Try block, it starts looking for and finds a Catch clause that receives an Exception exception. Therefore, this Catch is executed. Here is an example of running the program:

Enter First Number:  1255.85
Enter Operation (+, -, *, or /): +
Enter Second Number: 704.62

1255.85 + 704.62 = 1960.47

Here is another example of running the same program:

Enter First Number:  1255.85
Enter Operation (+, -, *, or /): @
Enter Second Number: 704.62

Invalid Operator: @
Your operation could not be performed

Here is another example of running the progvram:

Enter First Number:  124.05
Enter Operation (+, -, *, or /): /
Enter Second Number: 0

124.05 / 0 = Infinity

Imagine that the user wants to perform a division. You need to tell the compiler what to do if the user enters the denominator as 0 (or 0.00). If this happens, the best option, and probably the only one you should consider is to display a message and get out. Fortunately, the .NET Framework provides the DivideByZeroException class to deal with an exception caused by division by zero. As done with the message passed to the Exception class, you can compose your own message and pass it to the DivideByZeroException(string message) constructor.

Exception is the parent of all exception classes. Therefore, if you write various catch blocks, the one that takes the Exception as argument must be the last.

Here is an example that catches two types of exceptions:

Imports System

Public Class Exercise
    Public Shared Sub main()
        Dim Number1 As Double, Number2 As Double
        Dim Operator As String
        Dim Result As Double

        Try
            Console.Write("Enter First Number:  ")
            Number1 = CDbl(Console.ReadLine())
            Console.Write("Enter Operation (+, -, *, or /): ")
            Operator = CStr(Console.ReadLine())
            Console.Write("Enter Second Number: ")
            Number2 = CDbl(Console.ReadLine())

            If Operator <> "+" And Operator <> "-" And _
                        Operator <> "*" And Operator <> "/" Then
                Throw New Exception(Operator)
            End If

            If Operator = "/" And Number2 = 0 Then
                Throw New DivideByZeroException("Division by zero is not allowed")
            End If

            If Operator = "+" Then
                Result = Number1 + Number2
            ElseIf Operator = "-" Then
                Result = Number1 - Number2
            ElseIf Operator = "*" Then
                Result = Number1 * Number2
            ElseIf Operator = "/" Then
                Result = Number1 / Number2
            Else
                Result = 0
            End If

            Console.WriteLine(vbCrLf & "{0} {1} {2} = {3}", _
                              Number1, Operator, Number2, Result)

            Console.WriteLine()
        Catch ex As DivideByZeroException
            Console.WriteLine(ex.Message)
        Catch ex As Exception
            Console.WriteLine("Invalid Operator: {0}", Operator)
            Console.WriteLine("Your operation could not be performed")
        End Try
    End Sub
End Class 

When running this program, if the user types a wrong operator, the compiler gets out the Try block and looks for a Catch that takes an Exception as argument. It finds the second and executes it.

If the user enters two valid numbers, then the compiler finds out if the operator entered was a forward slash “/” used to perform a division. If the user wants to perform a division, the compiler finds out if the second operand, the denominator, is 0. If it is, we create a DivideByZeroException instance and pass our own message to it. Based on this exception, the compiler gets out of the Try block and starts looking for a Catch block that considers a DivideByZeroException exception. It finds it in the first catch. Therefore, the compiler executes it.

Here is an example of executing the program:

Enter First Number:  1288.24
Enter Operation (+, -, *, or /): *
Enter Second Number: 4.06

1288.24 * 4.06 = 5230.2544

Here is another example of executing the same program:

Enter First Number:  125.85
Enter Operation (+, -, *, or /): /
Enter Second Number: 5.05

125.85 / 5.05 = 24.9207920792079

Here is another example of executing the same program:

Enter First Number:  808.82
Enter Operation (+, -, *, or /): /
Enter Second Number: 0
Division by zero is not allowed

Exceptions Nesting

The calculator simulator we have studied so far performs a division as one of its assignments. We learned that, in order to perform any operation, the compiler must first make sure that the user has entered a valid operator. Provided the operator is one of those we are expecting, we also must make sure that the user typed valid numbers. Even if these two criteria are met, it was possible that the user enter 0 for the denominator. The block that is used to check for a non-zero denominator depends on the exception that validates the operators.

You can create an exception inside of another. This is referred to as nesting an exception. This is done by applying the same techniques we used to nest conditional statements. This means that you can write an exception that depends on, and is subject to, another exception. To nest an exception, write a Try block in the body of the parent exception. The nested Try block must be followed by its own Catch(es) clause. To effectively handle the exception, make sure you include an appropriate Throw in the Try block. Here is an example:

Imports System

Public Class Exercise
    Public Shared Sub main()
        Dim Number1 As Double, Number2 As Double
        Dim Operator As String
        Dim Result As Double

        Try
            Console.Write("Enter First Number:  ")
            Number1 = CDbl(Console.ReadLine())
            Console.Write("Enter Operation (+, -, *, or /): ")
            Operator = CStr(Console.ReadLine())
            Console.Write("Enter Second Number: ")
            Number2 = CDbl(Console.ReadLine())
            Console.WriteLine()

            If Operator <> "+" And Operator <> "-" And _
                        Operator <> "*" And Operator <> "/" Then
                Throw New Exception(Operator)
            End If

            If Operator = "+" Then
                Result = Number1 + Number2
                Console.WriteLine(vbCrLf & "{0} {1} {2} = {3}", _
                                  Number1, Operator, Number2, Result)

            ElseIf Operator = "-" Then
                Result = Number1 - Number2
                Console.WriteLine(vbCrLf & "{0} {1} {2} = {3}", _
                                  Number1, Operator, Number2, Result)

            ElseIf Operator = "*" Then
                Result = Number1 * Number2
                Console.WriteLine(vbCrLf & "{0} {1} {2} = {3}", _
                                  Number1, Operator, Number2, Result)

            ElseIf Operator = "/" Then
                Try
                    If Number2 = 0 Then
                        Throw New DivideByZeroException("Division by zero is not allowed")
                    End If

                    Result = Number1 / Number2
                    Console.WriteLine(vbCrLf & "{0} {1} {2} = {3}", _
                                      Number1, Operator, Number2, Result)

                Catch ex As DivideByZeroException
                    Console.WriteLine(ex.Message)
                End Try

            Else
                    Result = 0
            End If

        Catch ex As Exception
            Console.WriteLine("Invalid Operator: {0}", Operator)
            Console.WriteLine("Your operation could not be performed")
        End Try
    End Sub
End Class

Here is an example of running the program:

Enter First Number:  125.55
Enter Operation (+, -, *, or /): /
Enter Second Number: 42.05

125.55 / 42.05 = 2.98573127229489

Here is another example of running the program:

Enter First Number:  125.55
Enter Operation (+, -, *, or /): /
Enter Second Number: 0

Division by zero is not allowed

 


Previous Copyright © 2005-2012 FunctionX