Home

Exception Handling

 

Introduction to Exceptions

 

An Overview

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.

 

Practical LearningPractical Learning: Introducing Exception Handling

  1. Start Notepad and type the following:
     
    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;
    	}
    }
  2. Save the file in a new folder named GCS4
  3. Save the file itself as OrderInfo.cs
  4. To start a new file, on the main menu of Notepad, click File -> New and type the following:
     
    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("====================================");
    		}
    	}
    }
  5. Save the file as OrderProcess.cs in the GCS4 folder
  6. To start a new file, on the main menu of Notepad, click File -> New and type the following:
     
    using System;
    
    namespace GeorgetownCleaningServices
    {
    	class Exercise
    	{
    		static void Main()
    		{
    			OrderProcessing Order = new OrderProcessing();
    
    			Order.ProcessOrder();
    		}
    	}
    }
  7. Save the file as Exercise.cs in the GCS4 folder
  8. Open the Command Prompt and change to the above GCS4 directory
  9. To create the application, type
    csc /out:"Georgetown Cleaning Services".exe OrderInfo.cs OrderProcess.cs Exercise.cs and press Enter
  10. To execute the application, type "Georgetown Cleaning Services" and press Enter. Here is an example:
     
    -/- Georgetown Cleaning Services -/-
    Enter Customer Name:  Perl
    Enter Customer Phone: 301-884-0912
    Enter the order date(mm/dd/yyyy):  08/04/0
    Enter the order time(hh:mm AM/PM): 10:26
    Number of Shirts:  12
    Number of Pants:   2
    Number of Dresses: 8
    
    The Total order is: $56.79
    Amount Tended? 70
    
    ====================================
    -/- Georgetown Cleaning Services -/-
    ====================================
    Customer:    Perl
    Home Phone:  301-884-0912
    Order Date:  Monday, August 04, 2003
    Order Time:  10:26 AM
    ------------------------------------
    Item Type  Qty Unit/Price Sub-Total
    ------------------------------------
    Shirts      12   0.95       11.40
    Pants        2   2.95        5.90
    Dresses      8   4.55       36.40
    ------------------------------------
    Total Order:   $53.70
    Tax Rate:      5.75 %
    Tax Amount:     $3.09
    Net Price:     $56.79
    ------------------------------------
    Amount Tended: $70.00
    Difference:    $13.21
    ====================================
  11. Return to Notepad

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. C# provides three keywords to handle an exception.

  1. Trying the normal flow: To deal with the expected behavior of a program, 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:
     
    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);
    		}
    	}
    }
  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. Combined with the try block, the 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:
     
    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
    		{
    
    		}
    	}
    }

 

 

Practical LearningPractical Learning: Introducing Vague Exceptions

  1. To introduce exceptions, access the OrderProcess.cs file and change it as follows:
     
    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
    		}
    	}
    }
  2. Save the file
  3. At the Command Prompt, to compile the application, type
    csc /out:"Georgetown Cleaning Services".exe OrderInfo.cs OrderProcess.cs Exercise.cs and press Enter
  4. To execute the application, type "Georgetown Cleaning Services" and press Enter
  5. Return to Notepad

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.

 

Practical LearningPractical Learning: Displaying Custom Messages

  1. To display custom messages to the user, change the OrderProcess.cs file as follows:
     
    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
    		}
    	}
    }
  2. Save the file
  3. At the Command Prompt, to compile the application, type
    csc /out:"Georgetown Cleaning Services".exe OrderInfo.cs OrderProcess.cs Exercise.cs and press Enter
  4. To execute the application, type "Georgetown Cleaning Services" and press Enter. Here is an example:
     
    -/- Georgetown Cleaning Services -/-
    Enter Customer Name:  Alexandria
    Enter Customer Phone: (102) 797-8382
    Enter the order date(mm/dd/yyyy):  04/02/2001
    Enter the order time(hh:mm AM/PM): 09:22 AM
    Number of Shirts:  6
    Number of Pants:   W
    The value you typed for the number of pair or pants is not a valid number
    Number of Dresses: 5
    
    The Total order is: $30.09
    Amount Tended? _100D
    You were asked to enter an amount of money but...
    
    ====================================
    -/- Georgetown Cleaning Services -/-
    ====================================
    Customer:    Alexandria
    Home Phone:  (102) 797-8382
    Order Date:  Monday, April 02, 2001
    Order Time:  9:22 AM
    ------------------------------------
    Item Type  Qty Unit/Price Sub-Total
    ------------------------------------
    Shirts       6   0.95        5.70
    Pants        0   2.95           0
    Dresses      5   4.55       22.75
    ------------------------------------
    Total Order:   $28.45
    Tax Rate:      5.75 %
    Tax Amount:     $1.64
    Net Price:     $30.09
    ------------------------------------
    Amount Tended:  $0.00
    Difference:    ($30.09)
    ====================================
  5. Return to Notepad

Exceptions in the .NET Framework

 

The Exception Class

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.

The Exception's Message

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.

 

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:

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

FormatException

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
 

Practical LearningPractical Learning: Using the FormatException Class

  1. Change the OrderProcess.cs file as follows:
     
    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
    		}
    	}
    }
  2. Save the file
  3. At the Command Prompt, to compile the application, type
    csc /out:"Georgetown Cleaning Services".exe OrderInfo.cs OrderProcess.cs Exercise.cs and press Enter
  4. To execute the application, type "Georgetown Cleaning Services" and press Enter
  5. Return to Notepad

The OverflowException Exception

A computer application receives, processes, and produces values on a regular basis as the program is running. To better manage these values, as we saw when studying variables and data types in Lesson 2, the compiler uses appropriate amounts of space to store its values. It is not unusual that either you the programmer or a user of your application 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.

ArgumentOutOfRangeException

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.

 

Practical LearningPractical Learning: Using an ArgumentOutOfRangeException Exception

  1. Change the OrderProcess.cs file as follows:
     
    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
    		}
    	}
    }
  2. Save the file
  3. At the Command Prompt, to compile the application, type
    csc /out:"Georgetown Cleaning Services".exe OrderInfo.cs OrderProcess.cs Exercise.cs and press Enter
  4. To execute the application, type "Georgetown Cleaning Services" and press Enter. Here is an example:
     
    -/- Georgetown Cleaning Services -/-
    Enter Customer Name:  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)
    ====================================
  5. Return to Notepad

The DivideByZeroException Exception

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

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

Techniques of Using Exceptions

 

Throwing an Exception

As mentioned above, the Exception class is equipped with a Message property that 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:

  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. 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.

 

Practical LearningPractical Learning: Identifying the Thrown Exception

  1. To catch various exceptions, change the OrderProcess.cs file as follows:
     
    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
    		}
    	}
    }
  2. Save the file
  3. At the Command Prompt, to compile the application, type
    csc /out:"Georgetown Cleaning Services".exe OrderInfo.cs OrderProcess.cs Exercise.cs and press Enter
  4. To execute the application, type "Georgetown Cleaning Services" and press Enter. Here is an example:
     
    -/- Georgetown Cleaning Services -/-
    Enter Customer Name:  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)
    ====================================
  5. Return to Notepad

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

Practical LearningPractical Learning: Nesting Exceptions

  1. To nest exceptions, change the OrderProcess.cs file as follows:
     
    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
    		}
    	}
    }
  2. At the Command Prompt, to compile the application, type
    csc /out:"Georgetown Cleaning Services".exe OrderInfo.cs OrderProcess.cs Exercise.cs and press Enter
  3. To execute the application, type "Georgetown Cleaning Services" and press Enter

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-2007 FunctionX, Inc. Next