Understanding the Exception Class


Understanding the Exception Class

Exceptions generally occur in .NET applications in response to abnormal conditions or other conditions that are not expected during the execution of the application. The common language runtime (CLR) provides an object-oriented exception model that relies on the Exception class and its derivatives.

A .NET application handles exceptions in a very similar manner as MFC applications by defining a protected block of code with the try keyword followed by one or more catch statements. When an exception condition occurs, the CLR or the application throws an exception, which is represented by an Exception class derivative. The exception is handled by the first catch statement in the exception-handling chain that handles the same exception class. For example, the following code segment shows a try..catch block with multiple catch statements:

 int Calculate( Size* pSize1, Size* pSize2, int nDivisor ) {    try    {       return( ((pSize1->get_Height() * pSize1->get_Width()) +                (pSize2->get_Height() * pSize2->get_Width())) / nDivisor );    }    catch ( DivideByZeroException* e )    {       ...    }    catch ( NullReferenceException* e )    {       ...    }    catch ( Exception* e )    {       ...    } } 

Each of the catch statements in the preceding example looks for a different type of exception. The first one is looking for a DivideByZeroException, which occurs when a division operation with a value of zero is performed. The second catch is looking for the NullReferenceException, which occurs when null pointers are accessed. The final catch will catch any other exception, because all exceptions are ultimately derived from the Exception class.

The Exception class is the exception base class; however, exceptions are broken into two main categories with their own base classes that are derived from Exception. The first is the SystemException, which is the base class for all predefined CLR exception classes.

The second is the ApplicationException, which is the base class for user-defined and application exception classes.

Where the MFC library uses the CException class as its exception base class and has several derivative exceptions, the .NET Framework has even more predefined exceptions. There are too many predefined exceptions to list in this hour's lesson; however, you can find information on all the exceptions in the online help provided with Visual Studio .NET.

The Exception class provides a number of properties to help identify the code location, the type, the help file, and the reason for the exception. The following properties are available as part of the Exception class: StackTrace, InnerException, Message, HelpLink, HResult, Source, and TargetSite.

Viewing the Stack Trace

The Exception class has the StackTrace property, which carries a stack trace that can be used to determine where the error occurs in the code. The StackTrace property is a String that lists all the called methods as well as the line numbers in the source file where the calls are made. Figure 13.1 shows the sample output from the StackTrace property when the code in Listing 13.1 is executed.

Figure 13.1. Sample StackTrace output when the divide-by-zero exception occurs.


Listing 13.1 DivByZeroException.cpp An Example of Using StackTrace to Show Where an Error Occurred
 1. // This is the main project file for VC++ application project  2. // generated using an Application Wizard.  3.  4. #include "stdafx.h"  5. #include <tchar.h>  6:  7. #using <mscorlib.dll>  8.  9. using namespace System; 10. 11. void Calculate( int nDiv ) 12. { 13.    try 14.    { 15.       // Force the DivideByZeroException to be thrown 16.       Console::WriteLine(10/nDiv); 17.    } 18.    catch (DivideByZeroException* e) 19.    { 20.       Console::WriteLine( "A divide by zero occurred:" ); 21.       Console::WriteLine( e->get_StackTrace() ); 22.    } 23. } 24. 25. // This is the entry point for this application 26. int _tmain(void) 27. { 28.     Calculate( 0 ); 29.     return 0; 30. } 

The StackTrace property provides an exact location of the line of code that caused the exception. This includes the function name, filename, and line number of the statement.

Using the InnerException Property

When you're handling exceptions, it is often useful to capture a series of related exceptions. Once inside an exception handler, it's possible to throw yet another exception. The result is a series or a chain of exceptions. The InnerException property of the Exception class points to the original or outermost exception in the chain inside the current catch block. For example, if you catch an exception, such as the DivideByZeroException, as shown in Listing 13.1, you can then throw an ArgumentException that has its InnerException property set to the original DivideByZeroException. The use of the InnerException property is shown in Listing 13.2.

Listing 13.2 DivByZeroException.cpp Demonstrated Use of the InnerException Property
 1: void Calculate( int nDiv )  2: {  3:    try  4:    {  5:       // Force the DivideByZeroException to be thrown  6:       Console::WriteLine(10/nDiv);  7:    }  8:    catch (DivideByZeroException* e)  9:    { 10:       Console::WriteLine( e->get_Message() ); 11: 12:       throw( new ArgumentException( "Divide by Zero", e ) ); 13:    } 14: } 15: 16: // This is the entry point for this application 20: int _tmain(void) 22: { 23:     try 24:     { 25:        Calculate( 0 ); 26:     } 27:     catch( ArgumentException* e ) 28:     { 29:        Console::WriteLine( e->InnerException->get_StackTrace() ); 30:     } 31:     return 0; 32: } 

Not all predefined exception classes allow you to set the InnerException property. It can only be set on the constructor of the exception; therefore, the exception class you use will determine whether you can use the InnerException property.

Finally Processing

When writing try..catch blocks, you'll sometimes need to have additional code that is executed regardless of an exception occurring or the code executing correctly. This is often the case when there is critical clean-up code that must execute. For instance, if your application opens a file or other type of resource within a function and an exception is thrown, if the file is not subsequently closed within the finally block, it will remain open until the user actually reboots his system. Exception handling in the .NET Framework provides the optional __finally block, which you can define for these situations.

Defining the __finally block is done by using the finally keyword at the end of a try..catch block, as shown in the following code segment:

 int _tmain(void) {     try     {        Calculate( 0 );     }     catch( ArgumentException* e )     {        Console::WriteLine( e->InnerException->get_StackTrace() );     }     __finally     {        Console::WriteLine( "Program Ending." );     }     return 0; } 

Defining a __finally block forces the statement to be written to the console even if an unhandled exception occurs.


Sams Teach Yourself Visual C++. NET in 24 Hours
Sams Teach Yourself Visual C++.NET in 24 Hours
ISBN: 0672323230
EAN: 2147483647
Year: 2002
Pages: 237

Similar book on Amazon

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