Chapter 11. Exceptions

What Are Exceptions?

An exception is aruntime error in a program that violates a system or application constraint, or a condition that is not expected to occur during normal operation. Examples are when a program tries to divide a number by 0 or tries to write to a read-only file. When these occur, the system catches the error and raises an exception.

If the program has not provided code to handle the exception, the system will halt the program.

For example, the following code raises an exception when it attempts to divide by 0:

static void Main()
   {
      int x = 10, y = 0;
      x /= y;              // Attempt to divide by zero--raises an exception
   }

When this code is run, the system displays the following error message:

Unhandled Exception: System.DivideByZeroException: Attempted to divide by zero.
What Are Exceptions?
at Exceptions_1.Program.Main() in C:ProgsExceptionsProgram.cs:line 12

The try Statement

The try statementallows you to designate blocks of code to be guarded for exceptions, and to supply code to handle them if they occur. The try statement consists of three sections, as shown in Figure 11-1.

  • The try block containsthe code that is being guarded for exceptions.

  • The catch clauses section containsone or more catch clauses. These are blocks of code to handle the exceptions. They are also known as exception handlers.

  • The finally block contains code to be executed under all circumstances, whether or not an exception is raised.

Structure of the try statement

Figure 11-1. Structure of the try statement

Handling the Exception

The previous example showed that attempting to divide by 0 causes an exception. You can modify the program to handle that exception by placing the code inside a try block and supplying a simple catch clause. When the exception is raised, it is caught and handled in the catch block.

static void Main()
   {
      int x = 10;

      try
      {
         int y = 0;
         x /= y;                    // Raises an exception
      }
      catch
      {
            ...                     // Code to handle the exception

         Console.WriteLine("Handling all exceptions - Keep on Running");
      }
   }

This code produces the following message. Notice that, other than the output message, there is no indication that an exceptionhas occurred.

Handling all exceptions - Keep on Running

The Exception Classes

There are many different types of exceptions that can occur in a program. The BCL defines a number of exception classes, each representing a specific type. When one occurs, the CLR

  • Creates an exception object for the type

  • Looks for an appropriate catch clause to handle it

All exception classes are ultimately derived from the System.Exception class. A portion of the exception inheritance hierarchy is shown in Figure 11-2.

Structure of the exception hierarchy

Figure 11-2. Structure of the exception hierarchy

An exception object contains read-only properties with information about the exception that caused it. Some of these properties are shown in Table 11-1.

Table 11-1. Selected Properties of an Exception Object

Property

Type

Description

Message

string

This property contains an error message explaining the cause of the exception.

StackTrace

string

This property contains information describing where the exception occurred.

InnerException

Exception

If the current exception was raised by another exception, this property contains a reference to the previous exception.

HelpLink

string

This property can be set by application-defined exceptions to give a URN or URL for information on the cause of the exception.

Source

string

If not set by an application-defined exception, this property contains the name of the assembly where the exception originated.

The catch Clause

The catch clause handles exceptions. There are three forms, allowing different levels of processing. The forms are shown in Figure 11-3.

The three forms of the catch clause

Figure 11-3. The three forms of the catch clause

The general catch clause can accept any exception, but can't determine the type of exception that caused it. This allows only general processing and cleanup for whatever exception might occur.

The specific catch clause form takes the name of an exception class as a parameter. It matches exceptions of the specified class or exception classes derived from it.

The specific catch clause with object form gives you the most information about the exception. It matches exceptions of the specified class, or exception classes derived from it. It also gives you an exception instance, called the exception variable, which is a reference to the exception object created by the CLR. You can access the exception variable's properties within the block of the catch clause to get specific information about the exception raised.

For example, the following code handles exceptions of type IndexOutOfRangeException. When one occurs, a reference to the actual exception object is passed into the code with parameter name e. The three WriteLine statements each read a string field from the exception object.

The three forms of the catch clause

Examples Using Specific catch Clauses

Going back to our divide-by-zero example, the following code modifies the previous catch clause to specifically handle exceptions of the DivideByZeroException class. While in the previous example, the catch clause would handle any exception raised in the try block, the current example will only handle those of the DivideByZeroException class.

Examples Using Specific catch Clauses

You could further modify the catch clause to use an exception variable. This allows you to access the exception object inside the catch block.

Examples Using Specific catch Clauses

This code produces the following output:

Message: Attempted to divide by zero.
Source:  Exceptions 1
Stack:      at Exceptions_1.Program.Main() in C:ProgsExceptions 1
Examples Using Specific catch Clauses
Exceptions 1Program.cs:line 14

The catch Clauses Section

The catch clauses section can contain multiple catch clauses. Figure 11-4 shows a summary of the catch clauses section.

Structure of the catch clauses section of a try statement

Figure 11-4. Structure of the catch clauses section of a try statement

When an exception is raised, the system searches the list of catch clauses in order, and the first catch clause that matches the type of the exception object is executed. Because of this, there are two important rules in ordering the catch clauses. They are the following:

  • The specific catch clauses must be ordered with the most specific exception types first, progressing to the most general. For example, if you declare an exception class derived from NullReferenceException, the catch clause for your derived exception type should be listed before the catch clause for NullReferenceException.

  • If there is a general catch clause, it must be last, after all specific catch clauses. Using the general catch clause is discouraged. You should use one of the specific catch clauses if at all possible. The general catch clause hides bugs by allowing the program to continue execution, and leaves the program in an unknown state.

The finally Block

If a program's flow of control enters a try statement that has a finally block, the finally block is always executed. The flow of control is illustrated in Figure 11-5.

  • If no exception occurs inside the try block, then at the end of the try block, control skips over any catch clauses and goes to the finally block.

  • If an exception occurs inside the try block, then any appropriate catch clauses in the catch clauses section are executed, followed by execution of the finally block.

Execution of the finally block

Figure 11-5. Execution of the finally block

Even if a try block has a return statement, the finally block will still always be executed before returning to the calling code. For example, in the following code, there is a return statement in the middle of the try block that is executed under certain conditions. This does not allow it to bypass the finally statement.

try
   {
      if (inVal < 10) {
         Console.Write("First Branch  - ");
         return;
      }
      else
         Console.Write("Second Branch - ");
   }
   finally
   { Console.WriteLine("In finally statement"); }

This code produces the following output when variable inVal has the value 5:

First Branch  - In finally statement

Finding a Handler for an Exception

When aprogram raises an exception, the system checks to see whether the program has provided a handler for it. The flow of control is illustrated in Figure 11-6.

  • If the exception occurred inside a try block, the system will check to see whether any of the catch clauses can handle the exception.

  • If an appropriate catch clause is found

    • The catch clause is executed.

    • If there is a finally block, it is executed.

    • Execution continues after the end of the try statement (i.e., after the finally block, or after the last catch clause if there is no finally block).

Exception with handler in current try statement

Figure 11-6. Exception with handler in current try statement

Searching Further

If the exception was raised ina section of code that was not guarded by a try statement, or if the try statement does not have a matching exception handler, the system will have to look further for a matching handler. It will do this by searching down the call stack, in sequence, to see whether there is an enclosing try block with a matching handler.

Figure 11-7 illustrates the search process. On the left of the figure is the calling structure of the code, and on the right is the call stack. The figure shows that Method2 is called from inside the try block of Method1. If an exception occurs inside the try block in Method2, the system does the following:

  • First, it checks to see whether Method2 has exception handlers that can handle the exception.

    • If so, Method2 handles it, and program execution continues.

    • If not, the system continues down the call stack to Method1, searching for an appropriate handler.

  • If Method1 has an appropriate catch clause, the system does the following:

    • Goes back to the top of the call stack—which is Method2

    • Executes Method2's finally block, and pops Method2 off the stack

    • Executes Method1's catch clause and its finally block

  • If Method1 doesn't have an appropriate catch clause, the system continues searching down the call stack.

Searching down the call stack

Figure 11-7. Searching down the call stack

General Algorithm

Figure 11-8 showsthe general algorithm for handling an exception.

The general algorithm for handling an exception

Figure 11-8. The general algorithm for handling an exception

Example of Searching Down the Call Stack

In the following code, Main startsexecution and calls method A, which calls method B. A description and diagram of the process are given after the code and in Figure 11-9.

class Program
   {
      static void Main()
      {
         MyClass MCls = new MyClass();
         try
            { MCls.A(); }
         catch (DivideByZeroException e)
            { Console.WriteLine("catch clause in Main()"); }
         finally
            { Console.WriteLine("finally clause in Main()"); }
         Console.WriteLine("After try statement in Main.");
         Console.WriteLine("            -- Keep running.");
       }
   }


   class MyClass
   {
      public void A()
      {
         try
            { B(); }
         catch (System.NullReferenceException)
            { Console.WriteLine("catch clause in A()"); }
         finally
            { Console.WriteLine("finally clause in A()"); }
      }


      void B()
      {
         int x = 10, y = 0;
         try
            { x /= y; }
         catch (System.IndexOutOfRangeException)
            { Console.WriteLine("catch clause in B()"); }
         finally
            { Console.WriteLine("finally clause in B()"); }
      }
   }

This code produces the following output:

finally clause in B()
finally clause in A()
catch clause in Main()
finally clause in Main()
After try statement in Main.
           -- Keep running.
  1. Main calls A, which calls B, which encounters a DivideByZeroException exception.

  2. The system checks B's catch section for a matching catch clause. Although it has one for IndexOutOfRangeException, it does not have one for DivideByZeroException.

  3. The system then moves down the call stack and checks A's catch section, where it finds that A also does not have a matching catch clause.

  4. The system continues down the call stack, and checks Main's catch clause section, where it finds that Main does have a DivideByZeroException catch clause.

  5. Although the matching catch clause has now been located, it is not executed yet. Instead, the system goes back to the top of the stack, executes B's finally clause, and pops B from the call stack.

  6. The system then moves to A, executes its finally clause, and pops A from the call stack.

  7. Finally, Main's matching catch clause is executed, followed by its finally clause. Execution then continues after the end of Main's try statement.

Searching the stack for an exception handler

Figure 11-9. Searching the stack for an exception handler

Throwing Exceptions

You can makeyour code explicitly raise an exception by using the throw statement. The syntax for the throw statement is the following:

throw ExceptionObject;

For example, the following code defines a method called PrintArg, which takes a string argument and prints it out. Inside the try block, it first checks to make sure that the argument is not null. If it is, it creates an ArgumentNullException instance and throws it. The exception instance is caught in the catch statement, and the error message is printed. Main calls the method twice: once with a null argument, and then with a valid argument.

Throwing Exceptions

This code producesthe following output:

Message:  Value cannot be null.
Parameter name: arg
Hi there!

Throwing Without an Exception Object

The throw statement can also be used without an exception object, inside a catch block.

  • This form rethrows the current exception, and the system continues its search for additional handlers for it.

  • This form can only be used inside a catch statement.

For example, the following code rethrows the exception from inside the first catch clause:

Throwing Without an Exception Object

This code produces thefollowing output:

Inner Catch:  Value cannot be null.
Parameter name: arg
Outer Catch:  Handling an Exception.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.146.176.145