![]() |
Exception Handling |
During the execution of a program, the computer will face two types of situations: those it is prepared to deal with and those it doesn’t like. Imagine you write a program that requests a number from the user: using System; public class Exercise { static void Main() { double Number; Console.Write("Type a number: "); Number = double.Parse(Console.ReadLine()); Console.WriteLine("\n{0} * 2 = {1}", Number, Number * 2); } } This is a classic easy program. 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 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 not know what to do. The only alternative the compiler would have is to send the problem to the operating system, hoping that the OS would know what to do. What actually happens is that, whenever the compiler is handed a task, it would try to perform the assignment. If it can’t perform the assignment, for any reason it is not prepared for, it would produce an error. As a programmer, if you can anticipate the type(s) of error(s) that could occur in your program, you can deal with it(them) by telling the compiler what to do when this type of error occurs. |
|
|
using System; namespace GeorgetownCleaningServices { struct OrderInformation { // Basic information about an order public string CustomerName; public string HomePhone; public DateTime OrderDate; public DateTime OrderTime; // Unsigned numbers to represent cleaning items public uint NumberOfShirts; public uint NumberOfPants; public uint NumberOfDresses; } } |
using System; namespace GeorgetownCleaningServices { class OrderProcessing { // Price of items const decimal PriceOneShirt = 0.95M; const decimal PriceAPairOfPants = 2.95M; const decimal PriceOneDress = 4.55M; const decimal TaxRate = 0.0575M; // 5.75% OrderInformation Order; // Each of these sub totals will be used for cleaning items private decimal SubTotalShirts; private decimal SubTotalPants; private decimal SubTotalDresses; // Values used to process an order private decimal TotalOrder; private decimal TaxAmount; private decimal SalesTotal; private decimal AmountTended; private decimal Difference; public void 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 = uint.Parse(Console.ReadLine()); Console.Write("Number of Pants: "); Order.NumberOfPants = uint.Parse(Console.ReadLine()); Console.Write("Number of Dresses: "); Order.NumberOfDresses = uint.Parse(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("\nThe 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(); } private void 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("===================================="); } } } |
using System; namespace GeorgetownCleaningServices { class Exercise { static void Main() { OrderProcessing Order = new OrderProcessing(); Order.ProcessOrder(); } } } |
-/- 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 ==================================== |
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. C# provides three keywords to handle an exception.
|
|
using System; namespace GeorgetownCleaningServices { class OrderProcessing { . . . No Change public void ProcessOrder() { . . . No Change // Request the quantity of each category of items try { Console.Write("Number of Shirts: "); Order.NumberOfShirts = uint.Parse(Console.ReadLine()); } catch { } try { Console.Write("Number of Pants: "); Order.NumberOfPants = uint.Parse(Console.ReadLine()); } catch { } try { Console.Write("Number of Dresses: "); Order.NumberOfDresses = uint.Parse(Console.ReadLine()); } catch { } . . . No Change // and request money for the order try { Console.Write("Amount Tended? "); AmountTended = decimal.Parse(Console.ReadLine()); } catch { } // Calculate the difference owed to the customer // or that the customer still owes to the store Difference = AmountTended - SalesTotal; ShowReceipt(); } private void ShowReceipt() { . . . No Change } } } |
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: using System; public class Exercise { static void Main() { double Number; try { Console.Write("Type a number: "); Number = double.Parse(Console.ReadLine()); Console.WriteLine("\n{0} * 2 = {1}", Number, Number * 2); } catch { Console.WriteLine("There was a problem with the program"); } } } 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. |
|
using System; namespace GeorgetownCleaningServices { class OrderProcessing { . . . No Change public void ProcessOrder() { . . . No Change // Request the quantity of each category of items try { Console.Write("Number of Shirts: "); Order.NumberOfShirts = uint.Parse(Console.ReadLine()); } catch { Console.WriteLine("The value you typed for the number of " + "shirts is not a valid number"); } try { Console.Write("Number of Pants: "); Order.NumberOfPants = uint.Parse(Console.ReadLine()); } catch { Console.WriteLine("The value you typed for the number of " + "pair or pants is not a valid number"); } try { Console.Write("Number of Dresses: "); Order.NumberOfDresses = uint.Parse(Console.ReadLine()); } catch { Console.WriteLine("The value you typed for the number of " + "dresses is not a valid number"); } // 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("\nThe Total order is: {0:C}", SalesTotal); // and request money for the order try { Console.Write("Amount Tended? "); AmountTended = decimal.Parse(Console.ReadLine()); } catch { Console.WriteLine("You were asked to enter an amount of money but..."); } // Calculate the difference owed to the customer // or that the customer still owes to the store Difference = AmountTended - SalesTotal; ShowReceipt(); } private void ShowReceipt() { . . . No Change } } } |
-/- 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) ==================================== |
Exceptions in the .NET Framework |
The Exception Class |
In traditionally-oriented error dealing languages such as C/C++ or Object Pascal, you could create any exception of your choice, including numeric or strings. To customize exception handling, you could also create your own class(es). Most libraries such as Borland's VCL and Microsoft's MFC also shipped with their own classes to handle exceptions. Event the Win32 library provides its type of mechanism to face errors. To support exception handling, 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. 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 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.
In exception handling, errors are dealt with in the catch section. To do this, use catch as if it were a method. This means that, on the right side of catch, opening a parenthesis, declare 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(Exception e) { // Deal with the exception here } 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: using System; public class Exercise { static void Main() { double Number; try { Console.Write("Type a number: "); Number = double.Parse(Console.ReadLine()); Console.WriteLine("\n{0} * 2 = {1}", Number, Number * 2); } catch(Exception ex) { Console.WriteLine(ex.Message); } } } Here is an example of running the program: Type a number: 38G.25 Input string was not in a correct format.
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: using System; public class Exercise { static void Main() { double Number = 0.00; try { Console.Write("Type a number: "); Number = double.Parse(Console.ReadLine()); Console.WriteLine("\n{0} * 2 = {1}", Number, Number * 2); } catch(Exception ex) { Console.WriteLine("The operation could not be carried because " + "the number you typed is not valid"); } } } 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: |
using System; public class Exercise { static void Main() { double Number = 0.00; try { Console.Write("Type a number: "); Number = double.Parse(Console.ReadLine()); Console.WriteLine("\n{0} * 2 = {1}", Number, Number * 2); } catch(Exception ex) { Console.WriteLine(ex.Message + "\nThe operation could not be carried because " + "the number you typed is not valid"); } } }
Here is an example of running the program:
Type a number: 78m4 Input string was not in a correct format. 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. You don't have to name the argument. Then, in the catch() section, 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
When studying data formatting in Lesson 4, 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, if your conversion fails, the program produces (in the next lessons, we will use he word "throw") an error. The error is of from the FormatException class. Here is a program that deals with a FormatException exception: using System; public class Exercise { static void Main() { double Number = 0.00; try { Console.Write("Type a number: "); Number = double.Parse(Console.ReadLine()); Console.WriteLine("\n{0} * 2 = {1}", Number, Number * 2); } catch(FormatException) { Console.WriteLine("You typed an invalid number"); } } } Here is an example of running the program: Type a number: 39W.68g You typed an invalid number |
|
using System; namespace GeorgetownCleaningServices { class OrderProcessing { . . . No Change public void ProcessOrder() { . . . No Change // Request the quantity of each category of items try { Console.Write("Number of Shirts: "); Order.NumberOfShirts = uint.Parse(Console.ReadLine()); } catch(FormatException) { Console.WriteLine("The value you typed for the number of " + "shirts is not a valid number"); } try { Console.Write("Number of Pants: "); Order.NumberOfPants = uint.Parse(Console.ReadLine()); } catch(FormatException) { Console.WriteLine("The value you typed for the number of " + "pair or pants is not a valid number"); } try { Console.Write("Number of Dresses: "); Order.NumberOfDresses = uint.Parse(Console.ReadLine()); } catch(FormatException) { Console.WriteLine("The value you typed for the number of " + "dresses is not a valid number"); } . . . No Change // Communicate the total to the user... Console.WriteLine("\nThe Total order is: {0:C}", SalesTotal); // and request money for the order try { Console.Write("Amount Tended? "); AmountTended = decimal.Parse(Console.ReadLine()); } catch(FormatException) { Console.WriteLine("You were asked to enter an amount of money but..."); } // Calculate the difference owed to the customer // or that the customer still owes to the store Difference = AmountTended - SalesTotal; ShowReceipt(); } private void ShowReceipt() { . . . No Change } } } |
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 provides an value that is beyond the allowed range based on the data type. For example, we saw that 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: using System; // An Exercise class class Exercise { static void Main() { byte NumberOfPages; Console.Write("Enter the number of pages of the newspaper: "); NumberOfPages = byte.Parse(Console.ReadLine()); Console.WriteLine("Number of Pages of the Newspaper: {0}\n", NumberOfPages); } } When a value beyond the allowable range is asked to be stored in memory, the compiler produces (the verb is "throws" as we will learn soon) an error of the OverflowException class. Here is an example of running the program: Enter the number of pages of the newspaper: 824 Unhandled Exception: System.OverflowException: Value was either too large or too small for an unsigned byte. at System.Byte.Parse(String s, NumberStyles style, IFormatProvider provider) at System.Byte.Parse(String s) at Exercise.Main() in c:\programs\msvcs .net 2003\project17\exercise.cs:line 11 As with the other errors, when this exception is thrown, you should take appropriate action.
Once again, when studying the techniques of converting or formatting values in Lesson 4, we saw that a value is passed to the Parse() method of its data type for analysis. For a primitive data type, the Parse() method scans the string and if the string cannot be converted into a valid character or number, the compiler usually throws a FormatException exception as we saw above. Other classes such as DateTime also use a Parse() method to scan the value submitted to it. For example, if you request a date value from the user, the DateTime.Parse() method scans the string to validate it. In US English, Parse() expects the user to type a string in the form m/d/yy or mm/dd/yy or mm/dd/yyyy. Consider the following program: using System; // An Exercise class class Exercise { static void Main() { DateTime DateHired; Console.Write("Enter Date Hired: "); DateHired = DateTime.Parse(Console.ReadLine()); Console.WriteLine("Date Hired: {0:d}\n", DateHired); } } If the user types a value that cannot be converted into a valid date, the compiler throws an ArgumentOutOfRangeException exception. Here is an example of running the above program: Enter Date Hired: 1244/04/258 Unhandled Exception: System.FormatException: String was not recognized as a valid DateTime. at System.DateTimeParse.Lex(Int32 dps, __DTString str, DateTimeToken dtok, DateTimeRawInfo raw, DateTimeResult result, DateTimeFormatInfo& dtfi) at System.DateTimeParse.Parse(String s, DateTimeFormatInfo dtfi, DateTimeStyles styles) at System.DateTime.Parse(String s, IFormatProvider provider, DateTimeStyles styles) at System.DateTime.Parse(String s, IFormatProvider provider) at System.DateTime.Parse(String s) at Exercise.Main() in c:\programs\msvcs .net 2003\project17\exercise.cs:line 11 One way you can avoid this is to guide the user but still take appropriate actions, just in case this error is thrown. |
|
using System; namespace GeorgetownCleaningServices { class OrderProcessing { . . . No Change public void 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(); try { Console.WriteLine("Enter the date this order was placed"); Console.Write("Enter Today's Day: "); int day = int.Parse(Console.ReadLine()); Console.Write("Enter Current Month: "); int month = int.Parse(Console.ReadLine()); Console.Write("Enter This Year: "); int year = int.Parse(Console.ReadLine()); Order.OrderDate = new DateTime(year, month, day); } catch(ArgumentOutOfRangeException) { Console.WriteLine("The date you entered is not valid - We will ignore it"); } try { Console.WriteLine("Enter the time this order was placed"); Console.Write("Enter the hour: "); int hour = int.Parse(Console.ReadLine()); Console.Write("Enter the minutes: "); int minute = int.Parse(Console.ReadLine()); Order.OrderDate = new DateTime(0, 0, 0, hour, minute, 0); } catch(ArgumentOutOfRangeException) { Console.WriteLine("The time value you entered is not valid - We will ignore it"); } // Request the quantity of each category of items try { Console.Write("Number of Shirts: "); Order.NumberOfShirts = uint.Parse(Console.ReadLine()); } catch(FormatException) { Console.WriteLine("The value you typed for the number of " + "shirts is not a valid number"); } try { Console.Write("Number of Pants: "); Order.NumberOfPants = uint.Parse(Console.ReadLine()); } catch(FormatException) { Console.WriteLine("The value you typed for the number of " + "pair or pants is not a valid number"); } try { Console.Write("Number of Dresses: "); Order.NumberOfDresses = uint.Parse(Console.ReadLine()); } catch(FormatException) { Console.WriteLine("The value you typed for the number of " + "dresses is not a valid number"); } . . . No Change // Communicate the total to the user... Console.WriteLine("\nThe Total order is: {0:C}", SalesTotal); // and request money for the order try { Console.Write("Amount Tended? "); AmountTended = decimal.Parse(Console.ReadLine()); } catch(FormatException) { Console.WriteLine("You were asked to enter an amount of money but..."); } // Calculate the difference owed to the customer // or that the customer still owes to the store Difference = AmountTended - SalesTotal; ShowReceipt(); } private void ShowReceipt() { . . . No Change } } } |
-/- Georgetown Cleaning Services -/- Enter Customer Name: Gertrude Ngana Enter Customer Phone: 208-923-8263 Enter the date this order was placed Enter Today's Day: 48 Enter Current Month: 6 Enter This Year: 2002 The date you entered is not valid - We will ignore it Enter the time this order was placed Enter the hour: 62 Enter the minutes: 380 The time value you entered is not valid - We will ignore it Number of Shirts: w The value you typed for the number of shirts is not a valid number Number of Pants: 8 Number of Dresses: 2 The Total order is: $34.58 Amount Tended? q42 You were asked to enter an amount of money but... ==================================== -/- Georgetown Cleaning Services -/- ==================================== Customer: Gertrude Ngana Home Phone: 208-923-8263 Order Date: Monday, January 01, 0001 Order Time: 12:00 AM ------------------------------------ Item Type Qty Unit/Price Sub-Total ------------------------------------ Shirts 0 0.95 0 Pants 8 2.95 23.60 Dresses 2 4.55 9.10 ------------------------------------ Total Order: $32.70 Tax Rate: 5.75 % Tax Amount: $1.88 Net Price: $34.58 ------------------------------------ Amount Tended: $0.00 Difference: ($34.58) ==================================== |
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.
As mentioned above, the Exception class is equipped with a Message property that carried 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: |
using System; class Exercise { static void Main() { double Operand1, Operand2; double Result = 0.00; char Operator; Console.WriteLine("This program allows you to perform an operation on two numbers"); try { Console.WriteLine("To proceed, enter a number, an operator, and a number:"); Operand1 = double.Parse(Console.ReadLine()); Operator = char.Parse(Console.ReadLine()); Operand2 = double.Parse(Console.ReadLine()); if( Operator != '+' && Operator != '-' && Operator != '*' && Operator != '/') throw new Exception(Operator.ToString()); switch(Operator) { case '+': Result = Operand1 + Operand2; break; case '-': Result = Operand1 - Operand2; break; case '*': Result = Operand1 * Operand2; break; case '/': Result = Operand1 / Operand2; break; default: Console.WriteLine("Bad Operation"); break; } Console.WriteLine("\n{0} {1} {2} = {3}", Operand1, Operator, Operand2, Result); } catch(Exception ex) { Console.WriteLine("\nOperation Error: {0} is not a valid operator", ex.Message); } } }
Here is an example of running the program:
This program allows you to perform an operation on two numbers To proceed, enter a number, an operator, and a number: 38.44 # 124.52 Operation Error: # is not a valid operator
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(Arg1) { // One Exception } catch(Arg2) { // Another Exception } The compiler would proceed in a top-down:
Multiple catches are written if or when a try block is expected to throw different types of errors. Once again, consider the following program: |
using System; class Exercise { static void Main() { double Operand1, Operand2; double Result = 0.00; char Operator; Console.WriteLine("This program allows you to perform an operation on two numbers"); try { Console.WriteLine("To proceed, enter a number, an operator, and a number:"); Operand1 = double.Parse(Console.ReadLine()); Operator = char.Parse(Console.ReadLine()); Operand2 = double.Parse(Console.ReadLine()); switch(Operator) { case '+': Result = Operand1 + Operand2; break; case '-': Result = Operand1 - Operand2; break; case '*': Result = Operand1 * Operand2; break; case '/': Result = Operand1 / Operand2; break; default: Console.WriteLine("Bad Operation"); break; } Console.WriteLine("\n{0} {1} {2} = {3}", Operand1, Operator, Operand2, Result); } catch(Exception ex) { Console.WriteLine("\nOperation Error: {0} is not a valid operator", ex.Message); } } }
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: |
using System; class Exercise { static void Main() { double Operand1, Operand2; double Result = 0.00; char Operator; Console.WriteLine("This program allows you to perform an operation on two numbers"); try { Console.WriteLine("To proceed, enter a number, an operator, and a number:"); Operand1 = double.Parse(Console.ReadLine()); Operator = char.Parse(Console.ReadLine()); Operand2 = double.Parse(Console.ReadLine()); if( Operator != '+' && Operator != '-' && Operator != '*' && Operator != '/') throw new Exception(Operator.ToString()); switch(Operator) { case '+': Result = Operand1 + Operand2; break; case '-': Result = Operand1 - Operand2; break; case '*': Result = Operand1 * Operand2; break; case '/': Result = Operand1 / Operand2; break; default: Console.WriteLine("Bad Operation"); break; } Console.WriteLine("\n{0} {1} {2} = {3}", Operand1, Operator, Operand2, Result); } catch(Exception ex) { Console.WriteLine("\nOperation Error: {0} is not a valid operator", ex.Message); } } }
When this program runs, if the user provides a valid number followed by a wrong operator, we call the Exception(string message) constructor and pass it a string converted from the character that was typed. Then, when the compiler gets out of the try block, it looks for and finds a catch clause that receives a character value. Therefore, this catch is executed. 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. This corresponds to the three periods of a C++' catch(...) block. 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: |
using System; class Exercise { static void Main() { double Operand1, Operand2; double Result = 0.00; char Operator; Console.WriteLine("This program allows you to perform an operation on two numbers"); try { Console.WriteLine("To proceed, enter a number, an operator, and a number:"); Operand1 = double.Parse(Console.ReadLine()); Operator = char.Parse(Console.ReadLine()); Operand2 = double.Parse(Console.ReadLine()); if( Operator != '+' && Operator != '-' && Operator != '*' && Operator != '/') throw new Exception(Operator.ToString()); if( Operator == '/' ) if( Operand2 == 0 ) throw new DivideByZeroException("Division by zero is not allowed"); switch(Operator) { case '+': Result = Operand1 + Operand2; break; case '-': Result = Operand1 - Operand2; break; case '*': Result = Operand1 * Operand2; break; case '/': Result = Operand1 / Operand2; break; default: Console.WriteLine("Bad Operation"); break; } Console.WriteLine("\n{0} {1} {2} = {3}", Operand1, Operator, Operand2, Result); } catch(DivideByZeroException ex) { Console.WriteLine(ex.Message); } catch(Exception ex) { Console.WriteLine("\nOperation Error: {0} is not a valid operator", ex.Message); } } }
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 the right values (a number, an operator, and another number), 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 takes a DivideByZeroException argument. It finds it in the first catch. Therefore, the compiler executes it. |
|
using System; namespace GeorgetownCleaningServices { class OrderProcessing { . . . No Change public void 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(); // To make it easy for the user, we will request different values for the date try { Console.WriteLine("Enter the date this order was placed"); Console.Write("Enter Today's Day: "); int day = int.Parse(Console.ReadLine()); Console.Write("Enter Current Month: "); int month = int.Parse(Console.ReadLine()); Console.Write("Enter This Year: "); int year = int.Parse(Console.ReadLine()); Order.OrderDate = new DateTime(year, month, day); } // This exception is thrown if the user types a value that cannot // be converted into a valid number catch(FormatException ex) { Console.WriteLine("Error: " + ex.Message + "\nThe value you entered " + "is not a valid number"); } // This exception is thrown if the values that user had typed cannot // produce a valid date value catch(ArgumentOutOfRangeException ex) { Console.WriteLine("The date you entered is not valid - " + "We will ignore it and use 1/1/0001 instead"); } try { Console.Write("Enter the time the order was placed(hh:mm AM/PM): "); Order.OrderTime = DateTime.Parse(Console.ReadLine()); } // This exception is thrown if the user typed values that cannot // produce a valid (date or) time value catch(FormatException) { Console.WriteLine("The time you entered is not valid and it will be refused"); } // Request the quantity of each category of items try { Console.Write("Number of Shirts: "); Order.NumberOfShirts = uint.Parse(Console.ReadLine()); } // This exception is thrown if the user typed a value that cannot // be recognized as a valid number catch(FormatException ex) { Console.WriteLine("Error: " + ex.Message + "\nThe value you typed for the number of " + "shirts is not valid"); } // This exception is thrown if the user types a negative value catch(OverflowException) { Console.WriteLine("The number you typed is negative but " + "we cannot accept a negative number of shirts"); } try { Console.Write("Number of Pants: "); Order.NumberOfPants = uint.Parse(Console.ReadLine()); } // This exception is thrown if the user typed a value that cannot // be recognized as a valid number catch(FormatException ex) { Console.WriteLine("Error: " + ex.Message + "\nThe value you typed for the number of " + "pair or pants is not valid"); } // This exception is thrown if the user types a negative value catch(OverflowException) { Console.WriteLine("The number you typed is negative and " + "we cannot accept it"); } try { Console.Write("Number of Dresses: "); Order.NumberOfDresses = uint.Parse(Console.ReadLine()); } // This exception is thrown if the user typed a value that cannot // be recognized as a valid number catch(FormatException ex) { Console.WriteLine("Error: " + ex.Message + "\nThe value you typed for the number of " + "dresses is not valid"); } // This exception is thrown if the user types a negative value catch(OverflowException) { Console.WriteLine("The number you typed is lower than the " + "acceptable number. It will be ignored"); } // Perform the necessary calculations . . . No Change ShowReceipt(); } private void ShowReceipt() { . . . No Change } } } |
-/- Georgetown Cleaning Services -/- Enter Customer Name: Andrew Palermo Enter Customer Phone: (240) 493-8935 Enter the date this order was placed Enter Today's Day: 12 Enter Current Month: 08 Enter This Year: 2002 Enter the time the order was placed(hh:mm AM/PM): 12:98 The value you entered is not a valid number Number of Shirts: 9 Number of Pants: -5 The number you typed is negative and we cannot accept it Number of Dresses: -12 The number you typed is lower than the acceptable number. It will be ignored The Total order is: $9.04 Amount Tended? W10.00F Error: Input string was not in a correct format. You were asked to enter an amount of money but you typed an invalid currency val ue ==================================== -/- Georgetown Cleaning Services -/- ==================================== Customer: Andrew Palermo Home Phone: (240) 493-8935 Order Date: Monday, August 12, 2002 Order Time: 12:00 AM ------------------------------------ Item Type Qty Unit/Price Sub-Total ------------------------------------ Shirts 9 0.95 8.55 Pants 0 2.95 0 Dresses 0 4.55 0 ------------------------------------ Total Order: $8.55 Tax Rate: 5.75 % Tax Amount: $0.49 Net Price: $9.04 ------------------------------------ Amount Tended: $0.00 Difference: ($9.04) ==================================== |
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(es) clause. To effectively handle the exception, make sure you include an appropriate throw in the try block. Here is an example: |
using System; class Exercise { static void Main() { double Operand1, Operand2; double Result = 0.00; char Operator; Console.WriteLine("This program allows you to perform an operation on two numbers"); try { Console.WriteLine("To proceed, enter a number, an operator, and a number:"); Operand1 = double.Parse(Console.ReadLine()); Operator = char.Parse(Console.ReadLine()); Operand2 = double.Parse(Console.ReadLine()); if( Operator != '+' && Operator != '-' && Operator != '*' && Operator != '/') throw new Exception(Operator.ToString()); switch(Operator) { case '+': Result = Operand1 + Operand2; Console.WriteLine("\n{0} + {1} = {2}", Operand1, Operand2, Result); break; case '-': Result = Operand1 - Operand2; Console.WriteLine("\n{0} - {1} = {2}", Operand1, Operand2, Result); break; case '*': Result = Operand1 * Operand2; Console.WriteLine("\n{0} * {1} = {2}", Operand1, Operand2, Result); break; case '/': // The following exception is nested in the previous try try { if(Operand2 == 0) throw new DivideByZeroException("Division by zero is not allowed"); Result = Operand1 / Operand2; Console.WriteLine("\n{0} / {1} = {2}", Operand1, Operand2, Result); } catch(DivideByZeroException ex) { Console.WriteLine(ex.Message); } break; } } catch(Exception ex) { Console.WriteLine("\nOperation Error: {0} is not a valid operator", ex.Message); } } }
Here is an example of running the program:
This program allows you to perform an operation on two numbers To proceed, enter a number, an operator, and a number: 245.55 / 0 Division by zero is not allowed
|
using System; namespace GeorgetownCleaningServices { class OrderProcessing { // Price of items const decimal PriceOneShirt = 0.95M; const decimal PriceAPairOfPants = 2.95M; const decimal PriceOneDress = 4.55M; const decimal TaxRate = 0.0575M; // 5.75% OrderInformation Order; // Each of these sub totals will be used for cleaning items private decimal SubTotalShirts; private decimal SubTotalPants; private decimal SubTotalDresses; // Values used to process an order private decimal TotalOrder; private decimal TaxAmount; private decimal SalesTotal; private decimal AmountTended; private decimal Difference; public void 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(); // To make it easy for the user, we will request different values for the date try { int month = DateTime.Today.Month; int day = DateTime.Today.Day; int year = DateTime.Today.Year; Console.WriteLine("Enter the date this order was placed"); try { Console.Write("Enter the year this order was placed: "); year = int.Parse(Console.ReadLine()); if( year < 1998 ) throw new ArgumentOutOfRangeException("This business started in 1998. " + "Any year prior to that value is invalid"); if( year > DateTime.Today.Year ) throw new ArgumentOutOfRangeException("Common... " + "We can't process an order that will occur in the future."); } // Exception to deal with if the user typed a non-digit in the value catch(FormatException ex) { Console.WriteLine("Error: " + ex.Message + "\nYou entered an invalid value for the year"); } // Exception to deal with if the user typed a negative number catch(ArgumentOutOfRangeException ex) { Console.WriteLine(ex.Message); } // Evaluate the value of the month try { Console.Write("Enter the month this order was placed: "); month = int.Parse(Console.ReadLine()); if( month < 1 ) throw new ArgumentOutOfRangeException("A negative month cannot be accepted"); if( month > 12 ) throw new ArgumentOutOfRangeException("Valid numbers of month must be from 1 to 12"); } // Exception to deal with if the user typed a non-digit in the value catch(FormatException ex) { Console.WriteLine("Error: " + ex.Message + "\nYou entered an invalid value for the month"); } // Exception to deal with if the user typed a negative number catch(ArgumentOutOfRangeException ex) { Console.WriteLine(ex.Message); } // Evaluate the value of the day try { Console.Write("Enter the day this order was placed: "); day = int.Parse(Console.ReadLine()); if( day < 0 ) throw new ArgumentOutOfRangeException("A negative day cannot be accepted"); } // Exception to deal with if the user typed a non-digit in the value catch(FormatException ex) { Console.WriteLine("Error: " + ex.Message + "\nYou entered an invalid value"); } // Exception to deal with if the user typed a negative number catch(ArgumentOutOfRangeException ex) { Console.WriteLine(ex.Message); } Order.OrderDate = new DateTime(year, month, day); } // This exception is thrown if the user types a value that cannot // be converted into a valid number catch(FormatException ex) { Console.WriteLine("Error: " + ex.Message + "\nThe value you entered " + "is not a valid number"); } . . . No Change ShowReceipt(); } private void ShowReceipt() { . . . No Change } } } |
Exceptions and Methods |
One of the most effective techniques used to deal with code is to isolate assignments. We have learned this when studying methods of classes. For example, the switch statement that was performing the operations in the “normal” version of our program can be written as follows: |
using System; class Exercise { static void Main() { double Number1, Number2; double Result = 0.00; char Operator; Console.WriteLine("This program allows you to perform an operation on two numbers"); try { Console.WriteLine("To proceed, enter"); Console.Write("First Number: "); Number1 = double.Parse(Console.ReadLine()); Console.Write("An Operator (+, -, * or /): "); Operator = char.Parse(Console.ReadLine()); if( Operator != '+' && Operator != '-' && Operator != '*' && Operator != '/' ) throw new Exception(Operator.ToString()); Console.Write("Second Number: "); Number2 = double.Parse(Console.ReadLine()); if( Operator == '/' ) if( Number2 == 0 ) throw new DivideByZeroException("Division by zero is not allowed"); Result = Calculator(Number1, Number2, Operator); Console.WriteLine("\n{0} {1} {2} = {3}", Number1, Operator, Number2, Result); } catch(FormatException) { Console.WriteLine("The number you typed is not valid"); } catch(DivideByZeroException ex) { Console.WriteLine(ex.Message); } catch(Exception ex) { Console.WriteLine("\nOperation Error: {0} is not a valid operator", ex.Message); } } static double Calculator(double Value1, double Value2, char Symbol) { double Result = 0.00; switch(Symbol) { case '+': Result = Value1 + Value2; break; case '-': Result = Value1 - Value2; break; case '*': Result = Value1 * Value2; break; case '/': Result = Value1 / Value2; break; } return Result; } }
You can still use regular methods along with methods that handle exceptions. As done in the Main() function, any method of a program can take care of its own exceptions that would occur in its body. Here is an example of an exception handled in a function: |
using System; class Exercise { static void Main() { double Number1, Number2; double Result = 0.00; char Operator; Console.WriteLine("This program allows you to perform an operation on two numbers"); try { Console.WriteLine("To proceed, enter"); Console.Write("First Number: "); Number1 = double.Parse(Console.ReadLine()); Console.Write("An Operator (+, -, * or /): "); Operator = char.Parse(Console.ReadLine()); if( Operator != '+' && Operator != '-' && Operator != '*' && Operator != '/' ) throw new Exception(Operator.ToString()); Console.Write("Second Number: "); Number2 = double.Parse(Console.ReadLine()); Result = Calculator(Number1, Number2, Operator); } catch(FormatException) { Console.WriteLine("The number you typed is not valid"); } catch(Exception ex) { Console.WriteLine("\nOperation Error: {0} is not a valid operator", ex.Message); } } static double Calculator(double Value1, double Value2, char Symbol) { double Result = 0.00; switch(Symbol) { case '+': Result = Value1 + Value2; Console.WriteLine("\n{0} + {1} = {2}", Value1, Value2, Result); break; case '-': Result = Value1 - Value2; Console.WriteLine("\n{0} - {1} = {2}", Value1, Value2, Result); break; case '*': Result = Value1 * Value2; Console.WriteLine("\n{0} * {1} = {2}", Value1, Value2, Result); break; case '/': // The following exception is nested in the previous try try { if(Value2 == 0) throw new DivideByZeroException("Division by zero is not allowed"); Result = Value1 / Value2; Console.WriteLine("\n{0} / {1} = {2}", Value1, Value2, Result); } catch(DivideByZeroException ex) { Console.WriteLine(ex.Message); } break; } return Result; } }
Isolating assignments and handing them to functions is a complete and important matter in the area of application programming. Consider a program that handles a simple exception such as this one: |
using System; class Exercise { static void Main() { double Number1, Number2; double Result = 0.00; char Operator = '/'; Console.WriteLine("This program allows you to perform a division of two numbers"); try { Console.WriteLine("To proceed, enter two numbers:"); Number1 = double.Parse(Console.ReadLine()); Number2 = double.Parse(Console.ReadLine());; if( Number2 == 0 ) throw new DivideByZeroException("Division by zero is not allowed"); Result = Number1 / Number2; Console.WriteLine("\n{0} / {1} = {2}", Number1, Number2, Result); } catch(DivideByZeroException ex) { Console.WriteLine(ex.Message); } } }
One of the ways you can use methods in exception routines is to have a central method that receives variables, and sends them to an external method. The external method tests the value of a variable. If an exception occurs, the external method displays or sends a throw. This throw can be picked up by the method that sent the error. Observe the following example that implements this scenario: |
using System; class Exercise { static void Main() { double Number1, Number2; Console.WriteLine("This program allows you to perform a division of two numbers"); try { Console.WriteLine("To proceed, enter two numbers:"); Number1 = double.Parse(Console.ReadLine()); Number2 = double.Parse(Console.ReadLine());; Division(Number1, Number2); } catch(DivideByZeroException ex) { Console.WriteLine(ex.Message); } } static void Division(double a, double b) { double Result; // If an exception occurred, if( b == 0 ) // then throw a string to the function caller throw new DivideByZeroException("Division by zero is not allowed"); Result = a / b; Console.WriteLine("\n{0} / {1} = {2}", a, b, Result); } }
In this program, the Division method receives two values that it is asked to perform a division with. The Division method analyzes the second argument that represents the denominator. If this argument is zero, an exception is found and the Division method throws a DivideByZeroException exception. A method can also be called to perform more than one test to eventually throw more than one exception. Such a method can (and should) be programmed to throw different types of exceptions. |
|
||
Previous | Copyright © 2004-2016, FunctionX, Inc. | Next |
|