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.
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
Learning: Introducing Exception Handling |
|
- 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 |
|
- To arrange the tab sequence, on the main menu, click View -> Tab Order
- On the form, click only the following controls whose squares have a white
background, in the indicated order:
- Press Esc
- 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";
}
}
- 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");
}
- 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");
}
- Resize the form to appear as follows:
- To execute the application, on the Standard toolbar, click the Start
Without Debugging button
- Delete the value in the quantity and type w
- Click Process
- Click Quit and close the form to return to your programming environment
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.
- 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();
}
}
- 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
Learning: Introducing Vague Exceptions |
|
- 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(...)
{
}
}
- 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
- Click Process
- 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";
}
}
Of course, your message may not be particularly clear
but this time, the program would not crash.
Practical
Learning: Displaying Custom Messages |
|
- 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.");
}
}
- Test the application with valid and invalid values. Here is an example:
- Close the form and return to your programming environment
Exceptions in the .NET Framework
|
|
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.
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;
}
}
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";
}
}
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
|
|
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";
}
}
Practical
Learning: Using the FormatException Class |
|
- 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
}
}
- 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
|
|
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";
}
}
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:
- Following the normal flow of the program, the compiler
enters the try block
- 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:
- The compiler gets out of the try
section
- 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
- 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
- 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";
}
}
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
Learning: Identifying the Thrown Exception |
|
- 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 "
L"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.");
}
}
- Test the application and return to your programming environment
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.
|
|