Exceptions

   

The .NET languages feature a general-purpose error processing system known as an exception mechanism . The exception mechanism is composed of two parts : throwing exceptions and catching them. To throw an exception means to signal an error; to catch one is to trap and handle an error that was previously thrown. Exceptions provide a uniform approach to signaling and processing errors, removing much of the burden of traditional error processing.

The term "exception" is short for "exceptional event." Exceptional events are those that disrupt the normal flow of program execution. Java and C++ programmers find the .NET language exception mechanism very similar to that of Java and C++, and will be throwing and catching exceptions in no time. If you're a VB or C programmer, you'll find learning to use the exception mechanism is not only necessary, but well worth the effort.

In languages without exception mechanisms, such as VB 6 and C, each function is responsible for signaling success or failure during execution. In many cases, the function does this by returning an integer value that the caller can test. Generally, if the return value of a function is zero, the function is executed without error. If a non-zero value is returned, an error may have occurred during execution.

However, not all routines return error codes, and those that do don't necessarily report errors in the same way. Some may return an error code, others might return a null value, and still others might set a global error variable. Such inconsistencies place a substantial burden on the programmer, who must learn the error-reporting mechanism employed by each routine and write the appropriate code to test for errors.

As a result, many programmers save time by testing for errors generated by critical routines only, not bothering with the others. In some cases, the programmer may not fully understand the routine in question and might handle errors incorrectly. In both cases, the integrity of the program suffers and error checking becomes a nuisance, if not a nightmare.

Using exceptions, the .NET languages provide developers with a consistent and relatively simple mechanism for signaling and dealing with errors. Exceptions provide methods with a way to stop program execution when an error is encountered, while allowing the method to notify its caller that a problem has occurred. If the caller chooses, it may ignore, or "duck" the exception, in which case the exception is passed down the call stack until it is dealt with. However, exceptions allow you to only temporarily pass the buck when an error is encountered , because you must deal with the error eventually.

Call Stack

A call stack is nothing more than the sequence of methods that have been invoked. For instance, if a method named drawShape() calls another method named drawCircle() , you have a pretty simple call stack. In this situation, drawShape() called the drawCircle() method. The drawShape() method is said to be at the "bottom" of the stack, whereas drawCircle() is at the "top."

However, drawCircle() might invoke another method named draw() . This then sits at the top of the call stack. And draw() might call another method, named paint() , and the call stack continues to grow. If an exception occurs in paint() , it could possibly be ignored by every method. As a result, the exception would then be passed all the way down the call stack.

If an exception isn't handled by a method, it's passed down the call stack to the method below it. If none of the methods in the call stack catches the exception by the time it reaches the bottom, and the method at the bottom doesn't catch it either, the program is aborted!

Somewhere along the way, the exception has to be caught and dealt with. If it isn't, the program is aborted. If you attempt to write a program that produces such a result, the compiler warns you about it.

Throwing Exceptions

Before an exception can be caught, it must be thrown. Exceptions can be thrown by any .NET code: your own code, code in the namespaces that come with .NET, code in namespaces written by others ”even the Common Runtime can throw exceptions that your programs must catch.

When an exception is thrown, the system receives a signal that an error has been generated. Exceptions are thrown to flag errors, which is something you do in your own programs to notify the system that an error has occurred. As soon as an exception is thrown, the runtime system searches for the matching catch clause to handle it.

The following syntax is used to throw exceptions:

VB

 throw new AnyExceptionObject 

C#

 throw new AnyExceptionObject(); 

JScript

 throw new AnyExceptionObject(); 

Regardless of what code raises an exception, the throw statement is always used. It takes a single argument: a throwable object. Throwable objects are instances of any subclass of an Exception class. In the C# example above, you instantiated a throwable object as follows :

 new AnyExceptionObject() // instantiate throwable object 

If you attempt to throw an object that isn't throwable, the compiler outputs an error message and refuses to complete the compilation process. Most exception objects you'll encounter are derived from the Exception class.

Throwing exceptions is only half the battle. To write effective programs, you must be able to catch exceptions as well.

Catching Exceptions

When an exception is thrown, the system immediately stops the current flow of program execution and looks for an exception handler to catch it. Searching backward through the call stack, a corresponding handler is started with the method where the error occurred.

The search continues down the call stack until a method containing an appropriate exception handler is found. The first handler encountered that has the same type as the thrown object is the one chosen to deal with the exception.

If the exception travels all the way down the call stack with no handler catching it, the program aborts execution. Typically, an error message is output to the terminal display in such cases. This, of course, assumes the exception is a runtime exception that can't be seen by the compiler.

Try-Catch Clause

To catch an exception, you must write an exception handler using the try-catch clause. For example, suppose you wanted to use a method called something such as Open () . To catch the exception that might result, you'd write the following try-catch clauses:

VB

 Try    MyConnection.Open()  Catch e As Exception    ' Handle the error here 

C#

 try  {    MyConnection.Open();  }  catch( Exception e )  {    // Handle the error here.  } 

JScript

 try  {    MyConnection.Open();  }  catch( Exception e )  {    // Handle the error here.  } 
Try Block

The first part of a try-catch clause, the try block, encloses those statements that may throw an exception. Here is the syntax of a typical C# try block:

 try  {      // Statements here might throw exceptions  } 

The only code in the example capable of throwing an exception is the MyConnection.Open() method. However, any number of legal statements that have the potential to throw an exception can be included in the try block.

If you have additional lines of code following MyConnection.Open() , they are not executed. Instead, MyConnection.Open() throws an exception that immediately stops program execution at that point, and then drops into the catch portion of the try-catch clause.

Catch Block

Following the try block are one or more catch blocks that you can use to trap and process exceptions. This is the C# catch block syntax:

 catch( Exception e )  {      // Handle the error here  } 

Although I only supplied one catch block in the MyConnection.Open() exception handler, any number could have been provided as follows:

 try  {      // Do stuff here that might throw an exception  }  catch( ExceptionType1 variable )  {      // Handle the exception that throws ExceptionType1  }  catch( ExceptionType2 variable )  {      // Handle the exception that throws ExceptionType2  }  catch( ExceptionType3 variable )  {      // Handle the exception that throws ExceptionType3  }  catch(ExceptionType4 variable )  {      // Handle the exception that throws ExceptionType4  } 

For example, suppose MyConnection.Open() is capable of throwing two different exceptions. In this case, you should provide a catch block for each of the possible exceptions:

 try  {      MyConnection.Open();  }  catch( ExceptionType1 e )  {      // Handle exception type 1  }  catch( ExceptionType2 e )  {      // Handle exception type 2  } 

The exception that is thrown is compared to the argument for each catch block in the order. (The catch argument can be an object or an interface type.) When a match is found, that catch block is executed. If no match is found, the exception propagates down the call stack, where it is compared against potential exception handlers until a match is found. And, as always, if no match is found, the program is aborted.

You can access the instance variables and methods of exceptions, just as for any other object. With this in mind, you can invoke the exception's Message property to get information on the exception.

Finally Block

The .NET languages' try-catch clause supports the use of an optional finally block. If defined, this is guaranteed to execute, regardless of whether an exception is thrown. As a result, you can use it to perform any necessary clean-up operation (closing files and streams, releasing system resources, and so on) that your methods require before the flow of control is transferred to another part of the program. This is the syntax of the finally block:

VB

 Finally      ' Statements here are executed before control transfers 

C#

 finally  {      // Statements here are executed before control transfers  } 

JScript

 finally  {      // Statements here are executed before control transfers  } 

In the context of the MyConnection.Open() example, a C# finally block might look like this:

 try  {      MyConnection.open();  }  catch( ExceptionType1 e )  {      // Handle the error  }  catch( ExceptionType2 e )  {      // Handle the other error  }  finally  {      // Do any clean-up work here  } 

Upon executing the finally block, control is transferred out of the try-catch clause. Typically, whatever event caused the try statement to terminate (falling through the execution of a break , continue , or return statement, or the propagation of an exception) dictates where the flow of control will resume.

The finally block could also execute a jump statement. This would cause another unconditional control transfer outside its code block, or cause another uncaught exception to be thrown. In either case, the original jump statement is abandoned , and the new unconditional control transfer (or exception) is processed .

All jump statements ( break , continue , return , and throw ) transfer control unconditionally. Whenever one of these statements causes control to bypass a finally block, the control transfer pauses while the finally part is executed, and continues if the finally part finishes normally.

   


Special Edition Using ASP. NET
Special Edition Using ASP.Net
ISBN: 0789725606
EAN: 2147483647
Year: 2002
Pages: 233

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