Home

Exception Handling

 

Introduction to Exceptions

 

An Overview

Imagine you want to write a program that requests a number from the user, multiplies the number by 2, and displays the result. The form of this program can be designed as follows:

Introduction to Exceptions

System::Void btnCalculate_Click(System::Object^  sender, System::EventArgs^  e)
{
	 double number, result;

	 number = double::Parse(txtNumber->Text);
	 result = number * 2;

	 txtResult->Text = result.ToString();
}

This looks like an easy request. When it comes up, the user is asked to simply type a number. The number would then be multiplied by 2 and display the result. Imagine that may be the Number text box is empty, or a user types something that is not a valid number, such as the name of a country or somebody’s telephone number. Since this program was expecting a number and it is not prepared to multiply a string to a number, it would produce an error.

Exception

Whenever the compiler is handed a task, it would try to perform the assignment. If it can’t proceed, for any reason it is not prepared for, it would cause an error. As a programmer, if you can anticipate the type of error that could occur in your program, you can catch the error yourself and deal with it by telling the compiler what to do when this type of error occurs.

Practical LearningPractical Learning: Introducing Exception Handling

  1. Start a new Windows Forms Application named GCS1 and design the form as follows:
     
    Control Name Text Additional Properties
    Form     Size: 374, 415
    Label   Customer Name:  
    TextBox txtCustomer1    
    Label   mm  
    Label   dd  
    Label   yyyy  
    Label   Order Date:  
    TextBox txtMM 1 TextAlign: Right
    TextBox txtDD 1 TextAlign: Right
    TextBox txtYYYY 1970 TextAlign: Right
    Label   Item Types  
    Label   Qty  
    Label   Unit Price  
    Label   Sub-Total  
    Label   Shirts  
    TextBox txtQtyShirts 0 TextAlign: Right
    TextBox txtUnitPriceShirts 1.05 TextAlign: Right
    TextBox txtSubTotalShirts 0.00 TextAlign: Right
    Label   Pants  
    TextBox txtQtyPants 0 TextAlign: Right
    TextBox txtUnitPricePants 2.75 TextAlign: Right
    TextBox txtSubTotalPants 0.00 TextAlign: Right
    Label   Dresses  
    TextBox txtQtyOther 0 TextAlign: Right
    TextBox txtUnitPriceOther 4.50 TextAlign: Right
    TextBox txtSubTotalOther 0.00 TextAlign: Right
    Button btnProcess Process  
    Label   Customer Name:  
    TextBox txtCustomer2    
    Label   Order date:  
    TextBox txtOrderDate    
    Label   Tax Rate:  
    TextBox txtTaxRate 5.75  
    Label   %  
    Button btnTax Tax  
    Label   Total Order:  
    TextBox txtTotalOrder 0.00 TextAlign: Right
    Label   Tax Amount:  
    TextBox txtTaxAmount 0.00 TextAlign: Right
    Label   Net Price:  
    TextBox txtNetPrice 0.00 TextAlign: Right
    Label   Amount Tended:  
    TextBox txtAmountTended 0.00 TextAlign: Right
    Button btnDifference Diff  
    Label   Difference:  
    TextBox txtDifference 0.00 TextAlign: Right
  2. To arrange the tab sequence, on the main menu, click View -> Tab Order
  3. On the form, click only the following controls whose squares have a white background, in the indicated order:
     
  4. Press Esc
  5. To process an order, double-click the Process button and implement its event as follows:
     
    #pragma once
    
    
    namespace GCS1 {
    
    	. . . No Change
    	public ref class Form1 : public System::Windows::Forms::Form
    	{
    	public:
    		Form1(void)
    		{
    			InitializeComponent();
    			//
    			//TODO: Add the constructor code here
    			//
    		}
    
    	. . . No Change
    
    
    	private:
    		/// <summary>
    		/// Required designer variable.
    		/// </summary>
    		System::ComponentModel::Container ^components;
    
    		// Order Information
    		String ^ CustomerName;
    		String ^ mm, ^ dd, ^ yyyy;
    
    		// Quantities of items
    		int NumberOfShirts;
    		int NumberOfPants;
    		int NumberOfOtherItems;
    
    		// Price of items
    		double PriceOneShirt, PriceAPairOfPants, PriceOneOther;
    
    		// Each of these sub totals will be used for cleaning items
    		double SubTotalShirts, SubTotalPants, SubTotalOthers;
    
    		// Values used to process an order
    		double TaxRate;
    		double TotalOrder, TaxAmount, SalesTotal;
    
    #pragma region Windows Form Designer generated code
    		/// <summary>
    		/// Required method for Designer support - do not modify
    		/// the contents of this method with the code editor.
    		/// </summary>
    		void InitializeComponent(void)
    		{
    			. . . No Change
    		}
    #pragma endregion
    private: System::Void btnProcess_Click(System::Object^  sender,
    				       System::EventArgs^  e)
    {
        if( this->btnProcess->Text == L"Process" )
        {
    	// Request order information from the user
    	CustomerName = this->txtCustomer1->Text;
    	mm   = this->txtMM->Text;
    	dd   = this->txtDD->Text;
    	yyyy = this->txtYYYY->Text;
    
    	// Request the quantity of each category of items
    	// Number of Shirts
    	NumberOfShirts  = int::Parse(this->txtQtyShirts->Text);
    	// Number of Pants
    	NumberOfPants   = int::Parse(this->txtQtyPants->Text);
    	// Number of Dresses
    	NumberOfOtherItems = int::Parse(this->txtQtyOther->Text);
    
    	// Unit Prices of items
    	PriceOneShirt     = double::Parse(this->txtUnitPriceShirts->Text);
    	PriceAPairOfPants = double::Parse(this->txtUnitPricePants->Text);
    	PriceOneOther     = double::Parse(this->txtUnitPriceOther->Text);
    
    	// Perform the necessary calculations
    	SubTotalShirts = NumberOfShirts  * PriceOneShirt;
    	SubTotalPants  = NumberOfPants   * PriceAPairOfPants;
    	SubTotalOthers = NumberOfOtherItems * PriceOneOther;
    
    	this->txtSubTotalShirts->Text = SubTotalShirts.ToString(L"C");
    	this->txtSubTotalPants->Text  = SubTotalPants.ToString(L"C");
    	this->txtSubTotalOther->Text  = SubTotalOthers.ToString(L"C");
    
    	// Calculate the "temporary" total of the order
    	TotalOrder      = SubTotalShirts + SubTotalPants + SubTotalOthers;
    
    	// Display the receipt
    	this->txtCustomer2->Text  = CustomerName;
    	this->txtOrderDate->Text  = String::Concat(mm, L"/", dd, L"/", yyyy);
    	this->txtTotalOrder->Text = TotalOrder.ToString(L"C");
    
    	this->Height = 416;
    	this->btnProcess->Text = "Reset";
        }
        else
        {
    	this->Height = 240;
    	this->txtCustomer1->Text = "";
    	this->txtMM->Text = "1";
    	this->txtDD->Text = "1";
    	this->txtYYYY->Text = "1970";
    	this->txtQtyShirts->Text = "0";
    	this->txtQtyPants->Text  = "0";
    	this->txtQtyOther->Text = "0";
    	this->txtSubTotalShirts->Text = "$0.00";
    	this->txtSubTotalPants->Text  = "$0.00";
    	this->txtSubTotalOther->Text = "$0.00";
    
    	this->btnProcess->Text = "Process";
        }
    }
  6. Double-click the Tax button and implement its event as follows:
     
    System::Void btnTax_Click(System::Object^  sender,
    			  System::EventArgs^  e)
    {
    	 // Get the tax rate
    	 TaxRate =  double::Parse(this->txtTaxRate->Text) / 100;
    
    	// Calculate the tax amount using a constant rate
    	TaxAmount       = TotalOrder * TaxRate;
    	// Add the tax amount to the total order
    	SalesTotal      = TotalOrder + TaxAmount;
    
    	this->txtTaxAmount->Text  = TaxAmount.ToString(L"C");
    	this->txtNetPrice->Text   = SalesTotal.ToString(L"C");
    }
  7. Double-click the Diff button and implement its event as follows:
     
    System::Void btnDifference_Click(System::Object^  sender,
    				 System::EventArgs^  e)
    {
    	double AmountTended, Difference;
    
    	// Request money for the order
    	AmountTended    = double::Parse(this->txtAmountTended->Text);
    
    	// Calculate the difference owed to the customer
    	// or that the customer still owes to the store
    	Difference      = AmountTended - SalesTotal;
    
    	this->txtDifference->Text = Difference.ToString(L"C");
    }
  8. Resize the form to appear as follows:
     
  9. To execute the application, on the Standard toolbar, click the Start Without Debugging button
     
  10. Delete the value in the quantity and type w
  11. Click Process
     
  12. Click Quit and close the form to return to your programming environment

Exceptional Behaviors

An exception is an unusual situation that could occur in your program. As a programmer, you should anticipate any abnormal behavior that could be caused by the user entering wrong information that could otherwise lead to unpredictable results. The ability to deal with a program’s eventual abnormal behavior is called exception handling. The C/C++ language provides four main keywords to handle an exception. At this time, we will review two of them: try and catch.

  1. Trying the normal flow: To deal with the expected behavior of a program, you use the try keyword as in the following syntax:

    try {Behavior}

    The try keyword is required. It lets the compiler know that you are attempting a normal flow of your program. The actual behavior that needs to be evaluated is included between an opening curly bracket “{“ and a closing curly bracket “}”. Inside of the brackets, implement the normal flow that the program must follow, at least for this section of the code. Here is an example:
     
    System::Void btnCalculate_Click(System::Object^  sender,
    				System::EventArgs^  e)
    {
    	 double number, result;
    
    	 try {
    		 number = double::Parse(txtNumber->Text);
    		 result = number * 2;
    
    		 txtResult->Text = result.ToString();
    	 }
    }
  2. Catching Errors: During the flow of the program as part of the try section, if an abnormal behavior occurs, instead of letting the program crash or instead of letting the compiler send the error to the operating system, you can transfer the flow of the program to another section that can deal with it. The syntax used by this section is:

    catch {WhatToDo}

    This section always follows the try section. There must not be any code between the try’s closing bracket and the catch section. The catch keyword is required and follows the try section. Unlike try, catch always appears like a function. That is, the catch keyword always has parentheses. Unlike a normal function, catch must always receive an argument. If you don't have an argument to pass, then you can type three periods in the parentheses. Eventually, we will know what types of arguments can be passed to catch and we will know what the three periods mean.

    Combined with the try block, the basic syntax of an exception would be:
     
    try
    {
    	// Try the program flow
    }
    catch(...)
    {
    	// Catch the exception
    }

    A program that includes a catch section would appear as follows:
     
    System::Void btnCalculate_Click(System::Object^  sender, 
    	System::EventArgs^  e)
    {
    	 double number, result;
    
    	 try {
    		 number = double::Parse(txtNumber->Text);
    		 result = number * 2;
    
    		 txtResult->Text = result.ToString();
    	 }
    	 catch(...)
    	 {
    	 }
    }

 

Practical LearningPractical Learning: Introducing Vague Exceptions

  1. To introduce exceptions, access the form's code and change the events of the buttons as follows:
     
    System::Void btnProcess_Click(System::Object^  sender,
                               System::EventArgs^  e)
    {
        if( this->btnProcess->Text == L"Process" )
        {
    	// Request order information from the user
    	CustomerName = this->txtCustomer1->Text;
    	mm   = this->txtMM->Text;
    	dd   = this->txtDD->Text;
    	yyyy = this->txtYYYY->Text;
    
    	// Request the quantity of each category of items
    	// Number of Shirts
    	try {
    	    NumberOfShirts  = int::Parse(this->txtQtyShirts->Text);
    	}
    	catch(...)
    	{
    	}
    	// Number of Pants
    	try {
    		NumberOfPants   = int::Parse(this->txtQtyPants->Text);
    	}
    	catch(...)
    	{
    	}
    			// Number of Dresses
    	try {
    		NumberOfOtherItems = int::Parse(this->txtQtyOther->Text);
    	}
    	catch(...)
    	{
    	}
    
    	// Unit Prices of items
    	
    	. . . No Change	
    }
    
    System::Void btnTax_Click(System::Object^  sender, System::EventArgs^  e)
    {
        // Get the tax rate
        try {
    	TaxRate =  double::Parse(this->txtTaxRate->Text) / 100;
    
    	// Calculate the tax amount using a constant rate
    	TaxAmount       = TotalOrder * TaxRate;
    	// Add the tax amount to the total order
    	SalesTotal      = TotalOrder + TaxAmount;
    
    	this->txtTaxAmount->Text  = TaxAmount.ToString(L"C");
    	this->txtNetPrice->Text   = SalesTotal.ToString(L"C");
        }
        catch(...)
        {
        }
    }
    
    System::Void btnDifference_Click(System::Object^  sender, 
    	System::EventArgs^  e)
    {
        double AmountTended, Difference;
    
        // Request money for the order
        try {
    	AmountTended    = double::Parse(this->txtAmountTended->Text);
    
    	// Calculate the difference owed to the customer
    	// or that the customer still owes to the store
    	Difference      = AmountTended - SalesTotal;
    
    	this->txtDifference->Text = Difference.ToString(L"C");
        }
        catch(...)
        {
        }
    }
  2. Execute the application. This time, type invalid values in the quantity text boxes and other text boxes where the user is supposed to enter some values
     
  3. Click Process
     
  4. Close the form and return to your programming environment

Exceptions and Custom Messages

As mentioned already, if an error occurs when processing the program in the try section, the compiler transfers 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:

System::Void btnCalculate_Click(System::Object^  sender, System::EventArgs^  e)
{
	 double number, result;

	 try {
		 number = double::Parse(txtNumber->Text);
		 result = number * 2;

		 txtResult->Text = result.ToString();
	 }
	 catch(...)
	 {
		 lblMessage->Text = L"Invalid Number";
	 }
}
Custom Message

Of course, your message may not be particularly clear but this time, the program would not crash.

Practical LearningPractical Learning: Displaying Custom Messages

  1. To display custom messages to the user, change the form's (header) file as follows:
     
    System::Void btnProcess_Click(System::Object^  sender,
                                  System::EventArgs^  e)
    {
        if( this->btnProcess->Text == L"Process" )
        {
    	// Request order information from the user
    	CustomerName = this->txtCustomer1->Text;
    	mm   = this->txtMM->Text;
    	dd   = this->txtDD->Text;
    	yyyy = this->txtYYYY->Text;
    
    	// Request the quantity of each category of items
    	// Number of Shirts
    	try {
    	    NumberOfShirts  = int::Parse(this->txtQtyShirts->Text);
    	}
    	catch(...)
    	{
    	    MessageBox::Show(L"The value you typed for the number of " 
    		             L"shirts is not a valid number."
    	                     L"\nPlease enter a natural number "
    			     Lsuch as 2 or 24 or even 248");
    	}
    	// Number of Pants
    	try {
    	    NumberOfPants   = int::Parse(this->txtQtyPants->Text);
    	}
    	catch(...)
    	{
    	    MessageBox::Show(L"The value you typed for the number of "
    				L"pair or pants is not a valid number."
    		L"\nPlease enter a natural number such as 2 or 24 or even 248");
    	}
    	// Number of Dresses
    	try {
    		NumberOfOtherItems = int::Parse(this->txtQtyOther->Text);
    	}
    	catch(...)
    	{
    	    MessageBox::Show(L"The value you typed for the number of " 
    				L"dresses is not a valid number."
    		L"\nPlease enter a natural number such as 2 or 24 or even 248");
    	}
    
    	// Unit Prices of items
    	try {
    	    PriceOneShirt = double::Parse(this->txtUnitPriceShirts->Text);
    	}
    	catch(...)
    	{
    	    MessageBox::Show(L"The value you entered for the unit price of "
    				L"a shirt is not a recognizable currency amount." 
    				L"\nOnly natural or decimal numbers are allowed. " 
    		L"Please consult the management to know the valid prices.");
    	}
    	try 
    	{
    	    PriceAPairOfPants = double::Parse(this->txtUnitPricePants->Text);
    	}
    	catch(...)
    	{
    	    MessageBox::Show(L"The value you entered for the unit price of "
    			L"a pair of pants is not a recognizable currency amount."
    			L"\nOnly natural or decimal numbers are allowed. " 
    			L"You can consult the management to find out about "
    			L"the allowable prices.");
    	}
    	try 
    	{
    	    PriceOneOther = double::Parse(this->txtUnitPriceOther->Text);
    	}
    	catch(...)
    	{
    	    MessageBox::Show(L"The value you entered for the unit price of "
    				L"a dress is not a valid amount."
    			L"\nYou must enter only a natural or a decimal number. "
    		L"For more information, please consult the management to get "
    				L"the right prices.");
    	}
    
    	. . . No Change
        }
        else
        {
    	. . . No Change
        }
    }
    
    private: System::Void btnTax_Click(System::Object^  sender, System::EventArgs^  e)
    {
        // Get the tax rate
        try {
    	TaxRate =  double::Parse(this->txtTaxRate->Text) / 100;
    
            // Calculate the tax amount using a constant rate
    	    TaxAmount       = TotalOrder * TaxRate;
    	// Add the tax amount to the total order
    	SalesTotal      = TotalOrder + TaxAmount;
    
    	this->txtTaxAmount->Text  = TaxAmount.ToString(L"C");
    	this->txtNetPrice->Text   = SalesTotal.ToString(L"C");
        }
        catch(...)
        {
    	MessageBox::Show(L"The value you entered is not recognized "
    	 		L"as a valid tax rate."
    			L"\nA valid tax rate is a value between 0 and 100.00"
    			L"\nPlease try again.");
        }
    }
    
    private: System::Void btnDifference_Click(System::Object^  sender, 
    	System::EventArgs^  e)
    {
        double AmountTended, Difference;
    
        // Request money for the order
        try {
    	AmountTended    = double::Parse(this->txtAmountTended->Text);
    
            // Calculate the difference owed to the customer
    	// or that the customer still owes to the store
    	Difference      = AmountTended - SalesTotal;
    
    	this->txtDifference->Text = Difference.ToString(L"C");
        }
        catch(...)
        {
    	MessageBox::Show(L"The value you entered for the amount " 
    	 	         L"tended is not valid. "
    			 L"Only natural or decimal numbers are allowed."
    			 L"Please try again.");
        }
    }
  2. Test the application with valid and invalid values. Here is an example:
     

  3. Return to Notepad

Exceptions in the .NET Framework

 

The Exception Class

With C/C++, you can create any exception of your choice, including numeric or strings. So far, to handle exceptions, we were passing three periods to the catch clause. The three periods are telling the compiler that we know that an error is likely to occur but we don't know what type of error it would be. Therefore, the three periods are used for lack of more information.

To customize exception handling, you can even create your own class(es). Before you do this, the .NET Framework provides a special class called Exception. Once the compiler encounters an error, the Exception class allows you to identify the type of error and take an appropriate action.

Exception mostly serves as the general class of exceptions. Based on this, in an application, instead of the three periods, you can pass an Exception handle to the catch clause. Here is an example:

System::Void btnCalculate_Click(System::Object^  sender, System::EventArgs^  e)
{
	 double number, result;

	 try {
		 number = double::Parse(txtNumber->Text);
		 result = number * 2;

		 txtResult->Text = result.ToString();
	 }
	 catch(Exception ^)
	 {
		 lblMessage->Text = L"Invalid Number";
	 }
}

Passing an Exception argument to catch is still a general way to catch an unknown but likely exception. Anticipating various types of problems that can occur in a program, Microsoft created various other classes 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 identify the type of 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

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:
System::Void btnCalculate_Click(System::Object^  sender, System::EventArgs^  e)
{
	 double number, result;

	 try {
		 number = double::Parse(txtNumber->Text);
		 result = number * 2;

		 txtResult->Text = result.ToString();
	 }
	 catch(Exception ^ e)
	 {
		 lblMessage->Text = e->Message;
	 }
}
An exception using the Exception.Message message
 

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 expression "correct format" 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:
System::Void btnCalculate_Click(System::Object^  sender, System::EventArgs^  e)
{
	 double number, result;

	 try {
		 number = double::Parse(txtNumber->Text);
		 result = number * 2;

		 txtResult->Text = result.ToString();
	 }
	 catch(Exception ^)
	 {
		 lblMessage->Text = L"The operation could not be carried";
	 }
}
An exception with a custom message

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

private: System::Void button1_Click(System::Object ^  sender, System::EventArgs ^  e)
{
	 double number, result;

	 try {
		 number = textBox1->Text->ToDouble(0);
		 result = number * 2;

		 textBox2->Text = result.ToString();
	 }
	 catch(Exception ^e)
	 {
		 MessageBox::Show(String::Concat(e->Message,
			              L"The operation could not be carried because "
	                      L"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 will regularly use in our applications.

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 handle to a catch() clause but you don't have to name the handle: you just have to indicate that the class is passed as a handle. Then, in the catch() section, display a custom message. The second option you have consists of using the throw keyword. We will try to always indicate the type of exception that could be thrown if something goes wrong in a program.

The FormatException Exception

Everything the user types into a control using the keyboard is primarily a string and 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, if your conversion fails, the program produces (we will use the word "throw") an error. The error is from the FormatException class.

Here is a program that deals with a FormatException exception:
System::Void btnCalculate_Click(System::Object^  sender, System::EventArgs^  e)
		 {
	 double number, result;

	 try {
		 number = double::Parse(txtNumber->Text);
		 result = number * 2;

		 txtResult->Text = result.ToString();
	 }
	 catch(FormatException ^)
	 {
		 lblMessage->Text  = L"The operation could not be carried";
	 }
}
The FormatException exception
 

Practical LearningPractical Learning: Using the FormatException Class

  1. Change the form's file as follows:
     
    System::Void btnProcess_Click(System::Object^  sender,
                                  System::EventArgs^  e)
    {
        if( this->btnProcess->Text == L"Process" )
        {
    	. . . No Change
    
    	try {
    	    NumberOfShirts  = int::Parse(this->txtQtyShirts->Text);
    	}
    	catch(FormatException ^)
    	{
    		. . . No Change
    	}
    	// Number of Pants
    	try {
    	    NumberOfPants   = int::Parse(this->txtQtyPants->Text);
    	}
    	catch(FormatException ^)
    	{
    	    . . . No Change
    	}
    	// Number of Dresses
    	try {
    	    NumberOfOtherItems = int::Parse(this->txtQtyOther->Text);
    	}
    	catch(FormatException ^)
    	{
    	    . . . No Change
    	}
    
    	// Unit Prices of items
    	try {
    		PriceOneShirt = double::Parse(this->txtUnitPriceShirts->Text);
    	}
    	catch(FormatException ^)
    	{
    	    . . . No Change
    	}
    	try 
    	{
    	    PriceAPairOfPants = double::Parse(this->txtUnitPricePants->Text);
    	}
    	catch(FormatException ^)
    	{
    	    MessageBox::Show(L"The value you entered for the unit price of "
    		L"a pair of pants is not a recognizable currency amount."
    			L"\nOnly natural or decimal numbers are allowed. " 
    			L"You can consult the management to find out about "
    			L"the allowable prices.");
    	}
    	try 
    	{
    	    PriceOneOther     = double::Parse(this->txtUnitPriceOther->Text);
    	}
    	catch(FormatException ^)
    	{
    		. . . No Change
    	}
    
    	. . . No Change
    }
    
    private: System::Void btnTax_Click(System::Object^  sender,
                                       System::EventArgs^  e)
    {
        // Get the tax rate
        try {
    	. . . No Change
        }
        catch(FormatException ^)
        {
    	. . . No Change
        }
    }
    
    private: System::Void btnDifference_Click(System::Object^  sender, 
    	System::EventArgs^  e)
    {
        double AmountTended, Difference;
    
        // Request money for the order
        try {
    	. . . No Change
        }
        catch(FormatException ^)
        {
    	. . . No Change
        }
    }	
  2. Execute the application and return to your programming environment

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, 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 provides an 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:

System::Void button1_Click(System::Object^  sender, System::EventArgs^  e)
{
	 Byte numberOfPages;

	 numberOfPages = Byte::Parse(this->textBox1->Text);
	 this->textBox2->Text = numberOfPages.ToString();
}

When a value beyond the allowable range is asked to be stored in memory, the compiler produces an error of the OverflowException class. Here is an example of running the program with a bad number:

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

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 computer processor. It is addressed by the operating system at its 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 produces a DivideByZeroException exception.

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 where you are anticipating the error, type the throw keyword followed by a new operator and an instance of the Exception class using the constructor that takes a string. Here is an example:

System::Void btnCalculate_Click(System::Object^  sender, System::EventArgs^  e)
{
	 double number1, number2;
	 double result = 0.00;
	 String ^ operation;

	try {
		number1   = double::Parse(this->txtNumber1->Text);
		operation = this->txtOperation->Text;
		number2   = double::Parse(this->txtNumber2->Text);

		if( !operation->Equals(L"+") &&
			!operation->Equals(L"-") &&
			!operation->Equals(L"*") &&
			!operation->Equals(L"/") )
			throw gcnew Exception(operation);

		if( operation->Equals(L"+") )
			result = number1 + number2;
		else if( operation->Equals(L"-") )
			result = number1 - number2;
		else if(operation->Equals(L"*") )
			result = number1 * number2;
		else if( operation->Equals(L"/") )
			result = number1 / number2;
		else
			lblMessage->Text = L"Bad Operation";

		this->txtResult->Text = result.ToString();
	}
	catch(Exception ^)
	{
		lblMessage->Text = L"Operation Error: Invalid operator";
	}
}
Calculator 1: Exception Handling
 

Catching Various Exceptions

In the above examples, 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 function 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(Exception1)
{
	// One Exception
}
catch(Exceptio2)
{
	// Another Exception
}

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. For example, in our calculator, we want to consider only the addition, the subtraction, the multiplication, and the division. It is also likely that the user may type one or two invalid numbers. This leads us to know that our program can produce at least two types of errors at this time. Based on this, we can address them using two catch clauses as follows:

System::Void btnCalculate_Click(System::Object^  sender, System::EventArgs^  e)
{
	 double number1, number2;
	 double result = 0.00;
	 String ^ operation;

	try {
		number1   = double::Parse(this->txtNumber1->Text);
		operation = this->txtOperation->Text;
		number2   = double::Parse(this->txtNumber2->Text);

		if( !operation->Equals(L"+") &&
			!operation->Equals(L"-") &&
			!operation->Equals(L"*") &&
			!operation->Equals(L"/") )
			throw gcnew Exception(operation);

		if( operation->Equals(L"+") )
			result = number1 + number2;
		else if( operation->Equals(L"-") )
			result = number1 - number2;
		else if(operation->Equals(L"*") )
			result = number1 * number2;
		else if( operation->Equals(L"/") )
			result = number1 / number2;
		else
			lblMessage->Text = L"Bad Operation";

		this->txtResult->Text = result.ToString();
	}
	catch(FormatException ^)
	{
		lblMessage->Text = L"You typed an invalid number.";
	}
	catch(Exception ^ ex)
	{
		lblMessage->Text = L"Operation Error: Invalid operator";
	}
}
Catching multiple exceptions

This program works fine as long as the user types two valid numbers and a valid arithmetic operator. Anything else, such an invalid number or an unexpected operator would cause an error to be thrown:

Obviously, various bad things could happen when this program is running. 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, one of the options 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. This corresponds to the three periods of a catch(...) block. Therefore, if you write various catch blocks, the one that takes the three periods as argument must be the last. If you provide a catch that takes an Exception class as argument, you can omit the three-period catch and provide the Exception catch as the last.

Practical LearningPractical Learning: Identifying the Thrown Exception

  1. To catch various exceptions, change the form's file as follows:
     
    System::Void btnProcess_Click(System::Object^  sender,
                                  System::EventArgs^  e)
    {
        if( this->btnProcess->Text == L"Process" )
        {
    	// Request order information from the user
    	CustomerName = this->txtCustomer1->Text;
    	
    	try {
    	    int mm, dd, yyyy;
    	    mm   = this->txtMM->Text;
    	    dd   = this->txtDD->Text;
    	    yyyy = this->txtYYYY->Text;
    	}
    	// This exception is thrown if the user types a value that cannot
    	// be converted into a valid number
    	catch(FormatException ^ex)
    	{
    		MessageBox::Show(String::Concat(L"Error: ", ex.Message,
    					L"\nThe value you entered ",
    					L"is not a valid number");
    	}
    	// This exception is thrown if the values that user had typed cannot
    	// produce a valid date value
    	catch(ArgumentOutOfRangeException ^)
    	{
    		MessageBox::Show(L"The date you entered is not valid - "
    				 L"Please try again!");
    	}
    
    	// Request the quantity of each category of items
    	// Number of Shirts
    	try {
    	    NumberOfShirts  = int::Parse(this->txtQtyShirts->Text);
    	}
    	catch(FormatException ^)
    	{
    	    MessageBox::Show(L"The value you typed for the number of " 
    		             L"shirts is not a valid number."
    		 L"\nPlease enter a natural number such as 2 or 24 or even 248");
    	}
    	// This exception is thrown if the user types a negative value
    	catch(OverflowException ^)
    	{
    	    MessageBox::Show(L"The number you typed is negative but "
    			L"we cannot accept a negative number of shirts");
    	}
    
    	// Number of Pants
    	try {
    	    NumberOfPants   = int::Parse(this->txtQtyPants->Text);
    	}
    	catch(FormatException ^)
    	{
    	    MessageBox::Show(L"The value you typed for the number of "
    		      	L"pair or pants is not a valid number."
    		L"\nPlease enter a natural number such as 2 or 24 or even 248");
    	}
    	catch(OverflowException)
    	{
    	    MessageBox::Show(L"The number you typed is negative but "
    			L"we cannot accept a negative number of shirts");
    	}
    
    	// Number of Dresses
    	try {
    	    NumberOfOtherItems = int::Parse(this->txtQtyOther->Text);
    	}
    	catch(FormatException ^)
    	{
    	    MessageBox::Show(L"The value you typed for the number of " 
    			L"other items is not a valid number."
    		L"\nPlease enter a natural number such as 2 or 24 or even 248");
    	}
    	catch(OverflowException ^)
    	{
    	    MessageBox::Show(L"The number you typed is negative but "
    			L"we cannot accept a negative number of other items");
    	}
    
    	// Unit Prices of items
    	try {
                PriceOneShirt     = double::Parse(this->txtUnitPriceShirts->Text);
    	    if(PriceOneShirt < 0 )
    		throw new Exception(L"Negative numbers are not "
    				    L"allowed for the price of a shirt");
    	}
    	catch(FormatException ^)
    	{
    	    MessageBox::Show(L"The value you entered for the unit price of "
    			L"a shirt is not a recognizable currency amount." 
    			L"\nOnly natural or decimal numbers are allowed. " 
    		L"Please consult the management to know the valid prices.");
    	}
    	catch(Exception ^ex)
    	{
    	    MessageBox::Show(ex->Message);
    	}
    
    	try 
    	{
    	    PriceAPairOfPants = double::Parse(this->txtUnitPricePants->Text);
    	    if( PriceAPairOfPants < 0 )
    		throw new Exception(L"Negative numbers are not allowed "
    				    L"for the price of a pair of pants");
    	}
    	catch(FormatException ^)
    	{
    	    MessageBox::Show(L"The value you entered for the unit price of "
    		  	L"a pair of pants is not a recognizable currency amount."
    			L"\nOnly natural or decimal numbers are allowed. " 
    			L"You can consult the management to find out about "
    			L"the allowable prices.");
    	}
    	catch(Exception ^ex)
    	{
    	    MessageBox::Show(ex->Message);
    	}
    
    	try 
    	{
    	    PriceOneOther     = double::Parse(this->txtUnitPriceOther->Text);
    	    if( PriceOneDress < 0 )
    		throw new Exception(L"Negative numbers are not allowed "
    				    L"for the price of a items");
    	}
    	catch(FormatException ^)
    	{
    	    MessageBox::Show(L"The value you entered for the unit price of "
    			L"a dress is not a valid amount."
    			L"\nYou must enter only a natural or a decimal number. "
    		L"For more information, please consult the management to get "
    			L"the right prices.");
    	}
    	catch(Exception ^ex)
    	{
    	    MessageBox::Show(ex->Message);
    	}
    
    	// Perform the necessary calculations
    	SubTotalShirts = NumberOfShirts  * PriceOneShirt;
    	SubTotalPants  = NumberOfPants   * PriceAPairOfPants;
    	SubTotalOthers = NumberOfOtherItems * PriceOneOther;
    
    	this->txtSubTotalShirts->Text = SubTotalShirts.ToString(L"C");
    	this->txtSubTotalPants->Text  = SubTotalPants.ToString(L"C");
    	this->txtSubTotalOther->Text  = SubTotalOthers.ToString(L"C");
    
    	// Calculate the "temporary" total of the order
    	TotalOrder      = SubTotalShirts + SubTotalPants + SubTotalOthers;
    
    	// Display the receipt
    	this->txtCustomer2->Text  = CustomerName;
    	this->txtOrderDate->Text  = String::Concat(mm, L"/", dd, L"/", yyyy);
    	this->txtTotalOrder->Text = TotalOrder.ToString(L"C");
    
    	this->Height = 416;
    	this->btnProcess->Text = "Reset";
        }
        else
        {
    	this->Height = 240;
    	this->txtCustomer1->Text = "";
    	this->txtMM->Text = "1";
    	this->txtDD->Text = "1";
    	this->txtYYYY->Text = "1970";
    	this->txtQtyShirts->Text = "0";
    	this->txtQtyPants->Text  = "0";
    	this->txtQtyOther->Text = "0";
    	this->txtSubTotalShirts->Text = "$0.00";
    	this->txtSubTotalPants->Text  = "$0.00";
    	this->txtSubTotalOther->Text = "$0.00";
    
    	this->btnProcess->Text = "Process";
        }
    }
    
    System::Void btnTax_Click(System::Object^  sender, System::EventArgs^  e)
    {
        // Get the tax rate
        try {
    	TaxRate =  double::Parse(this->txtTaxRate->Text) / 100;
            
            if(PriceOneShirt < 0 )
    	  throw new Exception(
    		L"Negative numbers are not allowed for a tax rate");
    		
            // Calculate the tax amount using a constant rate
            TaxAmount       = TotalOrder * TaxRate;
    	// Add the tax amount to the total order
    	SalesTotal      = TotalOrder + TaxAmount;
    
    	this->txtTaxAmount->Text  = TaxAmount.ToString(L"C");
    	this->txtNetPrice->Text   = SalesTotal.ToString(L"C");
        }
        catch(FormatException ^)
        {
    	MessageBox::Show(L"The value you entered is not recognized "
    	 		L"as a valid tax rate."
    			L"\nA valid tax rate is a value between 0 and 100.00"
    			L"\nPlease try again.");
        }
        catch(Exception ^ex)
        {
    	MessageBox::Show(ex->Message);
        }
    }
    
    private: System::Void btnDifference_Click(System::Object^  sender, 
    		System::EventArgs^  e)
    {
        double AmountTended, Difference;
    
        // Request money for the order
        try {
    	AmountTended    = double::Parse(this->txtAmountTended->Text);
    
            // Calculate the difference owed to the customer
    	// or that the customer still owes to the store
    	Difference      = AmountTended - SalesTotal;
    
    	this->txtDifference->Text = Difference.ToString(L"C");
        }
        catch(FormatException ^)
        {
    	MessageBox::Show(L"The value you entered for the amount " 
    		    L"tended is not valid. "
    			L"Only natural or decimal numbers are allowed."
    			L"Please try again.");
        }
    }	
  2. Test the application and return to your programming environment

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. The exception that could result from a zero denominator depends on the user first entering a valid number for the denominator.

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 clause(s). To effectively handle the exception, make sure you include an appropriate throw in the try block.

 
 

Previous Copyright © 2007, yevol.com Next