Exception Handling in C

Team-Fly    

 
.NET and COM Interoperability Handbook, The
By Alan Gordon
Table of Contents
Chapter Four.  A Quick Introduction to C#

Exception Handling in C#

One area where both C# and the .NET Framework significantly improve on C++ and COM/Win32 is in the area of exception handling. C# supports the same structured try/catch statements for exception handling that C++ has, but it adds a few key improvements. To use C# exception handling, simply enclose the code that may throw an exception inside a try block and then add catch blocks for each type of exception that you would like to handle. An example is as follows :

 try {     Console.WriteLine("Enter the numerator");     string strNumerator=Console.ReadLine();     int intNumerator=int.Parse(strNumerator);     Console.WriteLine("Enter the denominator");     string strDenominator=Console.ReadLine();     int intDenominator=int.Parse(strDenominator);     int intResult=intNumerator/intDenominator;     Console.WriteLine("The result is: {0}", intResult); } catch (Exception e) {     Console.WriteLine(       "The exception type is: {0}",e.GetType());     Console.WriteLine(       "The exception message is: {0}",e.Message);     Console.WriteLine(       "The stack trace is: {0}",e.StackTrace); } 

The code here reads a numerator and a denominator from the console, converts the two inputs into integer numbers , and then divides the numerator by the denominator. There are a number of things that can go wrong with this code. The user could type in a value for the numerator or denominator that is not a number. Users could also enter zero for the denominator. If anything does go wrong with the code in a try block, the flow of control will jump to the catch block immediately following the try block. In this example, I print out the type of the exception, the message associated with the exception, and a stack trace.

The object that is thrown and caught in an exception statement is just an instance of a class that derives from System.Exception . The base class library contains a number of Exception classes, and you can also define you own by deriving a new class from System.Exception .

In most cases, you will not just want to return the message associated with the exception to the reader. You probably will want to present a user-friendly message to the user and perhaps perform some special processing in response to each particular type of exception. You can have a separate catch block for each type of exception that your code may throw. The code that follows has a catch block for the DivideByZeroException and another for FormatException (which is the exception that the code will throw if the user does not enter a number). Notice that I also still have a handler for the generic Exception class. The handler for the generic Exception class will catch any errors other than the divide by zero and format errors.

 try {     Console.WriteLine("Enter the numerator");     string strNumerator=Console.ReadLine();     int intNumerator=int.Parse(strNumerator);     Console.WriteLine("Enter the denominator");     string strDenominator=Console.ReadLine();     int intDenominator=int.Parse(strDenominator);     int intResult=intNumerator/intDenominator;     Console.WriteLine("The result is: {0}", intResult); } catch (DivideByZeroException e) {     Console.WriteLine("You attempted to divide by zero");     Console.WriteLine("The exception message is: {0}",         e.Message); } catch (FormatException e) {     Console.WriteLine("You must enter a number");     Console.WriteLine("The exception message is: {0}",         e.Message); } catch (Exception e) {     Console.WriteLine("The exception type is: {0}",         e.GetType());     Console.WriteLine("The exception message is: {0}",         e.Message);     Console.WriteLine("The stack trace is: {0}",         e.StackTrace); } 

You must define your exception handlers with the handlers for the more specific exceptions first and the most generic handler last. If you attempt to put the handler for a more generic exception class ahead of one of its derived types, the compiler will print the following error:

 A previous catch clause already catches all exceptions of this or a super type 

In some cases, you will want to handle an exception by performing some processing and then throwing the error again so that the method above the current method in the call stack can handle the error too. You can do this with the following code:

 try { //... code omitted for clarity } catch (Exception e) {     Console.WriteLine(e.Message);     throw e; } 

However, a better way to write this code is as follows:

 try { //... code omitted for clarity } catch (Exception e) {     Console.WriteLine(e.Message);     throw; } 

The difference has to do with the stack trace. When you write "throw e", the compiler treats this as if you are throwing the exception identified by "e" for the first time. Therefore, the stack trace looks as though the exception originated at the method that contains the "throw e" statement. Using the throw statement without an exception object means to rethrow the current exception. An example will make this clearer, so consider the following code:

 class TestClass {     static void Main(string[] args)     {         try         {             OuterMethod();         }         catch(Exception e)         {             Console.WriteLine(e.StackTrace);         }     }     static void OuterMethod()     {         try         {             GenException();         }         catch(Exception e)         {             throw e;         }     }     static void GenException()     {         try         {         //... code that generates an exception         }         catch (Exception e)         {             Console.WriteLine(e.Message);             Throw e;         }     } } 

Here I have a method call GenException that contains some logic that may throw an exception. GenException is called by a method called OuterMethod, which is called from the main routine. If you run this program, you will see that the stack trace from the catch block in the main method will look as follows:

 OuterMethod   Main 

The stack trace does not include the GenException method, which is where the exception actually originated. The problem is that the "throw e" call in OuterMethod created a new exception. If you changed the definition of OuterMethod to the following:

 static void OuterMethod() {     try     {       GenException();     }     catch(Exception e)     {       throw;     } } 

you will now get the followingcorrectstack trace:

 GenException   OuterMethod     Main 

So far, I have looked at catching (and rethrowing exceptions). Throwing exceptions from within your own code is also quite simple in C#. First, you have to decide what exception class to use for the exception that you will be throwing. You can use one of the predefined exception classes, or you can create your own exception class. The System namespace contains a number of exception classes that are generic enough to use in your own code. Table 4-12 lists just a few of them.

Table 4-12. Useful exception classes

Class Name

Description

ApplicationException

Thrown when a nonfatal application error occurs. Use this class as the base class for your custom, nonfatal exception classes.

ArgumentException

Thrown when an invalid argument is passed to a method.

ArgumentNullException

Thrown when a null argument is passed to a method. This exception class derives from ArgumentException.

ArgumentOutOfRangeException

Thrown when an argument to a method is not within the allowable range. This exception class derives from ArgumentException.

InvalidOperationException

Thrown when a method call is not valid for the current state of an object.

NotImplementedException

Thrown when a method that is called on an object has not been implemented.

You can use one of these classes or derive your own class from one of these or the other exception classes in the System namespace.

Regardless of whether you are using a built-in exception class or an exception class that you created, you throw an exception by creating a new instance of the Exception class and then using the throw keyword with the new instance as an argument. The following code shows how you would create a new Exception class:

 public enum RejectionReasons {     IncomeTooLow,     PaymentsTooHigh,     CreditScoreTooLow, } class LoanException : ApplicationException {     public LoanException(RejectionReasons aReason) :         base("You loan was rejected")     {         mReason=aReason;     }     public override string Message     {         get         {             string strRetval;             switch (mReason)             {             case RejectionReasons.IncomeTooLow:                 strRetval=                     "Your income is too low";                 break;             case RejectionReasons.PaymentsTooHigh:                 strRetval=                     "Too much debt";                 break;             case RejectionReasons.CreditScoreTooLow:                 strRetval=                     "Your credit score too low";                 break;             default:                 strRetval="Unknown reason";                 break;             }             return strRetval;         }     }     private RejectionReasons mReason; } 

Notice that I overrode the Message property to display a string that is consistent with the rejection reason. The following code shows how to use this exception from within your code:

 float fltIncome, fltLoanAmt; string strIncome, strLoanAmt; Console.WriteLine("Enter your requested loan amount"); strLoanAmt=Console.ReadLine(); fltLoanAmt=float.Parse(strLoanAmt); Console.WriteLine("Enter your annual income"); strIncome=Console.ReadLine(); fltIncome=float.Parse(strIncome); if (fltLoanAmt/fltIncome > 3)       throw new LoanException(RejectionReasons.IncomeTooLow); 

Team-Fly    
Top
 


. Net and COM Interoperability Handbook
The .NET and COM Interoperability Handbook (Integrated .Net)
ISBN: 013046130X
EAN: 2147483647
Year: 2002
Pages: 119
Authors: Alan Gordon

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net