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. at Exceptions_1.Program.Main() in C:ProgsExceptionsProgram.cs:line 12
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.
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
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.
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 |
---|---|---|
|
| This property contains an error message explaining the cause of the exception. |
|
| This property contains information describing where the exception occurred. |
|
| If the current exception was raised by another exception, this property contains a reference to the previous exception. |
|
| This property can be set by application-defined exceptions to give a URN or URL for information on the cause of the exception. |
|
| If not set by an application-defined exception, this property contains the name of the assembly where the exception originated. |
The catch
clause handles exceptions. There are three forms, allowing different levels of processing. The forms are shown in Figure 11-3.
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.
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.
You could further modify the catch
clause to use an exception variable. This allows you to access the exception object inside the catch
block.
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 Exceptions 1Program.cs:line 14
The catch
clauses section can contain multiple catch
clauses. Figure 11-4 shows a summary of the catch
clauses section.
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.
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.
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
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).
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.
Figure 11-8 showsthe general algorithm for handling an exception.
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.
Main
calls A
, which calls B
, which encounters a DivideByZeroException
exception.
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
.
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.
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.
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.
The system then moves to A
, executes its finally
clause, and pops A
from the call stack.
Finally, Main
's matching catch
clause is executed, followed by its finally
clause. Execution then continues after the end of Main
's try
statement.
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.
This code producesthe following output:
Message: Value cannot be null. Parameter name: arg Hi there!
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:
This code produces thefollowing output:
Inner Catch: Value cannot be null. Parameter name: arg Outer Catch: Handling an Exception.
3.146.176.145