Theme index -- Keyboard shortcut: 'u'  Previous theme in this lecture -- Keyboard shortcut: 'p'  Next slide in this lecture -- Keyboard shortcut: 'n'Exception Handling

A complete PDF version of the text book is now available. The PDF version is an almost complete subset of the HTML version (where only a few, long program listings have been removed). See here.

36.  Exceptions and Exception Handling in C#

Chapter 33, Chapter 34, and Chapter 35 have provided a context for this chapter. Warmed up in this way, we will now discuss different aspects of exceptions and exception handling in C#.

36.1 Exceptions in a C# program36.7 Propagation of exceptions in C#
36.2 The try-catch statement C#36.8 Raising and throwing exceptions in C#
36.3 Handling exceptions in C#36.9 Try-catch with a finally clause
36.4 The hierarchy of exceptions in C#36.10 Rethrowing an exception
36.5 The class System.Exception in C#36.11 Raising an exception in an exception handler
36.6 Handling more than one type of exception in C#36.12 Recommendations about exception handling
 

36.1.  Exceptions in a C# program
Contents   Up Previous Next   Slide Annotated slide Aggregated slides    Subject index Program index Exercise index 

Let us start with an simple example. Several variants of the example will appear throughout this chapter. In Program 36.1 the int table, declared and instantiated in line 6, is accessed by the expression M(table, idx) in line 9. In M we happen to address a non-existing element in the table. Recall that an array of 6 elements holds the elements table[0].. . table[5]. Therefore the cell addressed by table[6] is non-existing. Consequently, the execution of M(table, idx) in line 9 causes an error (index out of range). This is a run-time error, because the error happens when the program is executing. You can see this as a contrast to compile-time errors, which are identified before the program starts. In C#, a run-time error is materialized as an exception, which is an instance of class Exception or one of its subclasses. After its creation the exception is thrown. Throwing an exception means that the exception object is brought to the attention of exception handlers (catchers, see Section 36.3) which have a chance to react on the error condition represented by the exception. In the example shown in Program 36.1 the thrown exception is not handled.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
using System;

class ExceptionDemo{

  public static void Main(){
    int[] table = new int[6]{10,11,12,13,14,15};
    int idx = 6;                                   
                                                   
    M(table, idx);
  }

  public static void M(int[] table, int idx){
    Console.WriteLine("Accessing element {0}: {1}", 
                       idx, table[idx]);           
  }

}
Program 36.1    A C# program with an exception.

The output of Program 36.1 is shown in Listing 36.2. The effect of the WriteLine command in line 13 never occurs, because the error happens before WriteLine takes effect. The output in Listing 36.2 is therefore produced exclusively by the unhandled exception. We can see that the exception is classified as an IndexOutOfRangeException, which is quite reasonable. We can also see the stack trace from the beginning of the program to the place where the exception is thrown: Main, M (read from bottom to top in Listing 36.2).

1
2
3
4
Unhandled Exception: System.IndexOutOfRangeException: 
 Index was outside the bounds of the array.
     at ExceptionDemo.M(Int32[] table, Int32 idx)
     at ExceptionDemo.Main()
Listing 36.2    Output from the C# program with an exception.

 

36.2.  The try-catch statement C#
Contents   Up Previous Next   Slide Annotated slide Aggregated slides    Subject index Program index Exercise index 

In Section 36.1 we illustrated an unhandled exception. The error occurred, the exception object was formed, it was propagated through the calling chain, but it was never reacted upon (handled).

We will now introduce a new control structure, which allows us to handle exceptions, as materialized by objects of type Exception. Handling an exception imply in some situations that we attempt to recover from the error which is represented by the exception, such that the program execution can continue. In other situations the handling of the exception only involves a few repairs or state changes just before the program terminates. This is typically done to save data or to close connections such that the program can start again when the error in the source program has been corrected.

The try-catch statement allows us handle certain exceptions instead of stopping the program

The syntax of the new control structure is as shown below.


try
  try-block
catch (exception-type-1 name)
  catch-block-1
catch (exception-type-2 name)
  catch-block-2
...
Syntax 36.1    The syntax of the try-catch statement C#

try-block and catch-block-i are indeed block statements. It means that braces {...} are mandatory after try and after catch. Even if only a single action happens in try or catch, the action must be dressed as a block.

Let us assume that we are interested in handling the exceptions that are caused by execution of some command c. This can be arranged by embedding c in a try-catch control structure. It can also be arranged if another command d, which directly or indirectly activates c, is embedded in a try-catch control structure.

If an exception of a given type occurs, it can be handled in one of the matching catch clauses. A catch clause matches an exception object e, if the type of e is a subtype of exception-type-i (as given in one of the catch clauses). The matching of exceptions and catch clauses are attempted in the order provided by the catch clauses in the try control structure. Notice that each catch clause in addition specifies a name, to which the given exception object will be bound (in the scope of the handler). The names in catch clauses are similar to formal parameter names in methods.

Syntax 36.1 does not reflect the whole truth. The names of exceptions, next to the exception types, may be missing. It is even possible to have a catch clause without specification of an exception type. There is also an optional finally clause, which we will discuss in Section 36.9.

 

36.3.  Handling exceptions in C#
Contents   Up Previous Next   Slide Annotated slide Aggregated slides    Subject index Program index Exercise index 

Now that we have introduced the try-catch control structure let us handle the exception in Program 36.1. In Program 36.3 - inside M - around the two activations of WriteLine, we introduce a try-catch construct. If an IndexOutOfRangeException occurs in the try part, the control will be transferred to the neighbor catch part in which we adjust the index (using the method AdjustIndex), and we print the result. The program now prints "We get element number 5: 15".

Notice that once we have left the try part, due to an error, we will not come back to the try part again, even if we have repaired the problem. In Program 36.3 this means that the Console.WriteLine call in 16-17 is never executed. After having executed the catch clause in line 19-23, line 24 is executed next, and M returns to Main.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
using System;

class ExceptionDemo{

  public static void Main(){
    int[] table = new int[6]{10,11,12,13,14,15};
    int idx = 6;

    M(table, idx);
  }

  public static void M(int[] table, int idx){
    try{                                                       
      Console.WriteLine("Accessing element {0}: {1}", 
                         idx, table[idx]);
      Console.WriteLine("Accessing element {0}: {1}", 
                         idx-1, table[idx-1]);
    } 
    catch (IndexOutOfRangeException e){                        
      int newIdx = AdjustIndex(idx,0,5);                       
      Console.WriteLine("We get element number {0}: {1}",      
                         newIdx, table[newIdx]);            
    }   
    Console.WriteLine("End of M");   
  }



  public static int AdjustIndex(int i, int low, int high){     
    int res;                                                   
    if (i < low)
      res = low;
    else if (i > high)
      res = high;
    else res = i;

    return res;
  }
}
Program 36.3    A C# program with a handled exception.

In the example above we handled the exception in the immediate neighborhood of the offending statement. It is also possible to handle the exception at a more remote place in the program, but always along the path of activated, non-completed methods. We will illustrate this in Section 36.7.


Exercise 9.1. Exceptions in Convert.ToDouble

The static methods in the static class System.Convert are able to convert values of one type to values of another type.

Consult the documentation of System.Convert.ToDouble. There are several overloads of this method. Which exceptions can occur by converting a string to a double?

Write a program which triggers these exceptions.

Finally, supply handlers of the exceptions. The handlers should report the problem on standard output, rethrow the exception, and then continue.

Solution


 

36.4.  The hierarchy of exceptions in C#
Contents   Up Previous Next   Slide Annotated slide Aggregated slides    Subject index Program index Exercise index 

In this section we will take a concrete look at the classification of exceptions in C#. Our general discussion of this topic can be found in Section 35.2.

The following shows an excerpt the Exception class tree in C#. The tree is shown by textual indentation. Thus, the classes ApplicationException and SystemException are sons (and subclasses) of Exception.

  • Exception

    • ApplicationException

      • Your own exception types

    • SystemException

      • ArgumentException

        • ArgumentNullException

        • ArgumentOutOfRangeException

      • DivideByZeroException

      • IndexOutOfRangeException

      • NullReferenceException

      • RankException

      • StackOverflowException

      • IOException

        • EndOfStreamException

        • FileNotFoundException

        • FileLoadException

Notice first that the Exception class tree is not the whole story. There are many more exception classes in the C# libraries than shown above.

Exceptions of type SystemException are thrown by the common language runtime (the virtual machine) if some error condition occurs. System exceptions are nonfatal and recoverable. As a programmer, you are also welcome to throw a SystemException object (or more precisely, an object of one of the subclasses of SystemException) from a program, which you are writing.

An ArgumentException can be thrown if a an operation receives an illegal argument. The programmer of the operation decides which arguments are legal and illegal. The two shown subclasses of ArgumentException reflect that the argument cannot be null and that the argument is outside its legal range respectively.

The DivideByZeroException occurs if zero is used as a divisor in a division. The IndexOutOfRangeException occurs if an an array is accessed with an index, which is outside the legal bounds. The NullReferenceExceptions occurs in an expression like ref.name where ref is null instead of a reference to an object. The RankException occurs if an array with the wrong number of dimensions is passed to an operation. The StackOverflowException occurs if the memory space devoted to non-completed method calls is exhausted. The IOException (in the namespace System.IO) reflects different kinds of errors related to file input and file output.

ApplicationExceptions are "thrown when a non-fatal application error occurs" (quote from MSDN). The common runtime system throws instances of SystemException, not ApplicationException. Originally, the exception classes that you program in your own code were intended to be subclasses of ApplicationException. In version 3.5 of the .NET framework, Microsoft recommends that your own exceptions are programmed as subclasses of Exception [exceptions-best-practices].

You are encouraged to identify and throw exceptions which are specializations of SystemException. By (re)using existing exception types, it becomes possible for the system, or for third-party program contributions, to catch the exceptions that you throw from your own code.


Exercise 9.2. Exceptions in class Stack

In the lecture about inheritance we specialized the abstract class Stack.

Now introduce exception handling in your non-abstract specialization of Stack. I suggest that you refine your own solution to the previous exercise. It is also possible to refine my solution.

More specifically, introduce one or more stack-related exception classes. The slide page "Raising and throwing exceptions in C#" tells you how to do it. Make sure to specialize the appropriate pre-existing exception class!

Arrange that Push on a full stack and that Pop/Top on an empty stack throw one of the new exceptions. Also, in the abstract stack class, make sure that ToggleTop throws an exception if the stack is empty, or if the stack only contains a single element.

Finally, in a sample client program such as this one, handle the exceptions that are thrown. In this exercises it is sufficient to report the errors on standard output.

Solution


Exercise 9.3. More exceptions in class Stack

In continuation of the previous exercise, we now wish to introduce the following somewhat unconventional handling of stack exceptions:

  • If you push an element on a full stack throw half of the elements away, and carry out the pushing.
  • If you pop/top an empty stack, push three dummy elements on the stack, and do the pop/top operation after this.

With these ideas, most stack programs will be able to terminate normally (run to the end).

I suggest that you introduce yet another specialization of the stack class, which specializes Push, Pop, and Top. The specialized stack operations should handle relevant stack-related exceptions, and delegate the real work to its superclass. Thus, in the specialized stack class, each stack operation, such as Push, you should embed base.push(el) in a try-catch control structure, which repairs the stack - as suggested above - in the catch clause.

Solution


 

36.5.  The class System.Exception in C#
Contents   Up Previous Next   Slide Annotated slide Aggregated slides    Subject index Program index Exercise index 

The class Exception is the common superclass of all exception classes, and therefore it holds all common data and operations of exceptions. In this section we will examine the class Exception in some details.

  • Constructors

    • Parameterless: Exception()

    • With an explanation: Exception(string)

    • With an explanation and an inner exception: Exception(string,Exception)

  • Properties

    • Message: A description of the problem (string)

    • StackTrace: The call chain from the point of throwing to the point of catching

    • InnerException: The exception that caused the current exception

    • Data: A dictionary of key/value pairs.

      • For communication in between functions along the exception propagation chain.

    • Others...

Exception is a class (and instances of class Exception represents a concrete error). Therefore there exists constructors of class Exceptions, which (as usual) are used for initialization of a newly allocated Exception object. (See Section 12.4 for a general discussion of constructors).

The most useful constructor in class Exception takes a string, which holds an intuitive explanation of the problem. This string will appear on the screen, if a thrown exception remains unhandled. The third constructor, of the form Exception(string,Exception), involves an inner exception. Inner exceptions will be discussed in Section 36.11.

As outlined above, an exception has an interface of properties (see Chapter 18) that give access to the data, which are encapsulated by the Exception object. You can access the message (originally provided as input to the constructor), the stack trace, a possible inner exception, and data in terms of a key-value dictionary (used to keep track of additional data that needs to travel together with the exception). For general information about dictionaries, see Chapter 46.

 

36.6.  Handling more than one type of exception in C#
Contents   Up Previous Next   Slide Annotated slide Aggregated slides    Subject index Program index Exercise index 

We now continue the example from Section 36.3. In this section we will see how to handle multiple types of exceptions in a single try-catch statement.

The scene of Program 36.4 is similar to the scene in Program 36.3. In the catch clauses of the try-catch control structure we handle NullReferenceException and DivideByZeroException. On purpose, we do not yet handle IndexOutOfRangeException. Just wait a moment...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
using System;

class ExceptionDemo{

  public static void Main(){
    int[] table = new int[6]{10,11,12,13,14,15};
    int idx = 6;

    M(table, idx);
  }

  public static void M(int[] table, int idx){
    try{                                                     
      Console.WriteLine("Accessing element {0}: {1}", 
                         idx, table[idx]);
    } 
    catch (NullReferenceException){                          
      Console.WriteLine("A null reference exception");
      throw;      // rethrowing the exception
    }   
    catch (DivideByZeroException){                           
      Console.WriteLine("Divide by zero");
      throw;      // rethrowing the exception                
    }

  }
}
Program 36.4    A C# program with an exception handling attempt - not a success.

When we run the program in Program 36.4 the two handlers do not match the exception that occurs (the IndexOutOfRangeException exception). Therefore the exception remains unhandled, and the program stops with the output shown in Listing 36.5.

Notice that we do not provide names of the exceptions in the catch clauses in Program 36.4. We could do so. But because the names are not used they would cause the compiler to issue warnings.

While we are here, let us dwell on the two catch clauses that actually appear in the try-catch statement in Program 36.4. The null reference exception describes the problem of executing r.f in state where r refers to null. The divide by zero exception describes the problem of executing a/b in state where b is zero. The catch clauses report the problems, but they do not handle them. Instead, both catch clauses rethrow the exceptions. This is done by throw in line 19 and 23. By rethrowing the exceptions, an outer exception handler (surrounding the try catch) or exception handlers along the dynamic calling chain will have a chance to make a repair. Reporting the exception is a typical temptation of the programmer. But the reporting itself does not solve the problem! Therefore you should rethrow the exception in order to let another part of the program have to chance to make an effective repair. Rethrowing of exceptions is discussed in Section 36.10.

1
2
3
4
5
Unhandled Exception: 
 System.IndexOutOfRangeException:
  Index was outside the bounds of the array.
   at ExceptionDemo.M(Int32[] table, Int32 idx)
   at ExceptionDemo.Main()
Listing 36.5    Output from the C# program with an unhandled exception.

It was really too bad that we did not hit the IndexOutOfRangeException exception in Program 36.4. In Program 36.6 we will make a better job.

We extend the catch clauses with two new entries. We add the IndexOutOfRangeException and we add the root exception class Exception. Notice that the more general exception classes should always occur at the rear end of the list of catch clauses. The reason is that the catch clauses are consulted in the order they appear. (If the Exception catcher was the first one, none of the other would ever have a chance to take effect).

In the concrete example, the IndexOutOfRangeException clause is the first that matches the thrown exception. (Notice that newly added Exception clause also matches, but we never get that far). Therefore we get the output shown in Listing 36.7.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
using System;

class ExceptionDemo{

  public static void Main(){
    int[] table = new int[6]{10,11,12,13,14,15};
    int idx = 6;

    M(table, idx);
  }

  public static void M(int[] table, int idx){
    try{                                                      
      Console.WriteLine("Accessing element {0}: {1}", 
                         idx, table[idx]);
    } 
    catch (NullReferenceException){                           
      Console.WriteLine("A null reference exception");        
      throw;      // rethrowing the exception                 
    }  
    catch (DivideByZeroException){
      Console.WriteLine("Divide by zero");
      throw;      // rethrowing the exception
    } 
    catch (IndexOutOfRangeException){                         
      int newIdx = AdjustIndex(idx,0,5);                      
      Console.WriteLine("We get element number {0}: {1}", 
                         newIdx, table[newIdx]);            
    }   
    catch (Exception){                                        
      Console.WriteLine("We cannot deal with the problem");   
      throw;     // rethrowing the exception
    }

  }



  public static int AdjustIndex(int i, int low, int high){
    int res;
    if (i < low)
      res = low;
    else if (i > high)
      res = high;
    else res = i;

    return res;
  }
}
Program 36.6    A C# program with an exception handling attempt - now successful.

1
We get element number 5: 15
Listing 36.7    Output from the C# program with a handled exception.

Handle specialized exceptions before general exceptions

 

36.7.  Propagation of exceptions in C#
Contents   Up Previous Next   Slide Annotated slide Aggregated slides    Subject index Program index Exercise index 

In the examples shown until now (see Program 36.3, Program 36.4, and Program 36.6) we have handled exceptions close to the place where they are thrown. This is not necessary. We can propagate an exception object to another part of the program, along the chain of the incomplete method activations.

In Program 36.8 there is a try-catch statement in M and (as a new thing) also in Main. The local catchers in M do not handle the actual exception (which still is of type index out of range). The handlers in Main do! When the error occurs in M, the local exception handlers all have a chance of handling it. They do not! Therefore the exception is propagated to the caller, which is Main. The catchers around the activation of M in Main have a relevant clause that deals with IndexOutOfRangeException. It handles the problem by use of the static method AdjustIndex, following which if calls M with the adjusted index. After having executed the catch clause in Main, the command after try-catch in Main is executed (line 16). The output of Program 36.8 is shown in Listing 36.9. Notice that the output line 'End of M' is related to the call of M(table, newIdx) in line 14 of Program 36.8.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
using System;

class ExceptionDemo{

  public static void Main(){
    int[] table = new int[6]{10,11,12,13,14,15};
    int idx = 6;

    try{                                                     
      M(table, idx);                                         
    }   
    catch (IndexOutOfRangeException){                        
      int newIdx = AdjustIndex(idx,0,5);
      M(table, newIdx);                                      
    }
    Console.WriteLine("End of Main"); 
  }

  public static void M(int[] table, int idx){
    try{                                                    
      Console.WriteLine("Accessing element {0}: {1}",       
                         idx, table[idx]);                  
    }                                                       
    catch (NullReferenceException){
      Console.WriteLine("A null reference exception");
      throw;      // rethrowing the exception
    }   
    catch (DivideByZeroException){
      Console.WriteLine("Dividing by zero");
      throw;     // rethrowing the exception
    }

    Console.WriteLine("End of M"); 
  }



  public static int AdjustIndex(int i, int low, int high){  
    int res;
    if (i < low)
      res = low;
    else if (i > high)
      res = high;
    else res = i;

    return res;
  }
}
Program 36.8    A C# program with simple propagation of exception handling.

1
2
3
Accessing element 5: 15
End of M
End of Main
Listing 36.9    Output from the C# program simple propagation.

In order to illustrate a longer error propagation chain, we now in Program 36.10 introduce the calling chain

MainMNP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
using System;

class ExceptionDemo{

  public static void Main(){
    int[] table = new int[6]{10,11,12,13,14,15};
    int idx = 6;

    Console.WriteLine("Main");
    try{
      M(table, idx);                            
    }   
    catch (IndexOutOfRangeException){           
      M(table, AdjustIndex(idx,0,5));           
    }
  }

  public static void M(int[] table, int idx){
    Console.WriteLine("M(table,{0})", idx);
    N(table,idx);                               
  }

  public static void N(int[] table, int idx){
    Console.WriteLine("N(table,{0})", idx);
    P(table,idx);                                
  }

  public static void P(int[] table, int idx){            
    Console.WriteLine("P(table,{0})", idx);              
    Console.WriteLine("Accessing element {0}: {1}",      
                       idx, table[idx]);
  }



  public static int AdjustIndex(int i, int low, int high){   
    int res;
    if (i < low)
      res = low;
    else if (i > high)
      res = high;
    else res = i;

    return res;
  }
}
Program 36.10    A C# program with deeper exception propagation chain.

The error occurs in P, and it is handled in Main. Here is what happens when the expression M(table, idx) in line 11 is executed:

  1. The method M calls method N , N calls P , and in P an exception is thrown.
  2. The error is propagated back from P to Main via N and M , because there are no (relevant) handlers in P , N or M .
  3. The exception is handled in Main , and as part of the handling M is called again: M(table, AdjustIndex(idx,0,5)) .
  4. As above, M calls N , N calls P , and P calls WriteLine . Now no errors occur.

Due to the tracing calls of WriteLine in Main, M, N, and P the output shown in Listing 36.11, in part, confirms the story told about. To obtain the full confirmation, consult Exercise 9.4.

1
2
3
4
5
6
7
8
Main
M(table,6)
N(table,6)
P(table,6)
M(table,5)
N(table,5)
P(table,5)
Accessing element 5: 15
Listing 36.11    Output from the C# program deeper exception propagation.

Notice the yo-yo effect caused by the error deep in the calling chain.


Exercise 9.4. Revealing the propagation of exceptions

We have written a program that reveals how exceptions are propagated. In the program output, we see that the calling chain is Main, M, N, P.

The program output does not, however, reveal that the chain is followed in reverse order in an attempt to find an appropriate exception handler.

Revise the program with handlers in M, N, and P that touch the exception without actually handling it. The handlers should reveal, on standard output, that P, N, and M are passed in an attempt to locate a relevant exception handler. Rethrow the exception in each case. See here how this can be done.

Solution


 

36.8.  Raising and throwing exceptions in C#
Contents   Up Previous Next   Slide Annotated slide Aggregated slides    Subject index Program index Exercise index 

The IndexOutOfRangeException, which we have worked with in the previous sections, was raised by the system, as part of an illegal array indexing. We will now show how to explicitly raise an exception in our own program. We will also see how to define our own subclass of class ApplicationException.

The syntax of throw appears in in Syntax 36.2 and a simple example is shown next in Program 36.12. Notice the athletic metaphor behind throwing and catching.


 throw exception-object
Syntax 36.2    The syntax of exception throwing in C#

1
2
3
...
throw new MyException("Description of problem");
...
Program 36.12    A throw statement in C#.

It is simple to define the class MyException as subclass of ApplicationException, which in turn is a subclass of Exception, see Section 36.4. Notice the convention that our own exception classes are subclasses of ApplicationException.

1
2
3
4
5
class MyException: ApplicationException{
  public MyException(String problem): 
    base(problem){
  }
}
Program 36.13    Definition of the exception class.

It is recommended to adhere to a coding style where the suffixes (endings) of exception class names are "...Exception".

 

36.9.  Try-catch with a finally clause
Contents   Up Previous Next   Slide Annotated slide Aggregated slides    Subject index Program index Exercise index 

A try-catch control structure can be ended with an optional finally clause. Thus, we really deal with a try-catch-finally control structure. In this section we will study the finally clause.

The syntax of try-catch-finally, shown in Syntax 36.3, is a natural extension of the try-catch control structure illustrated in Syntax 36.1. try-block, catch-block, and finally-block are all block statements. As explained in Section 36.2 is means that braces {...} are mandatory after try, catch, and finally.


try
  try-block
catch (exception-type name)
  catch-block
...
finally
  finally-block
Syntax 36.3    The syntax of the try-catch-finally statement C#

At least one catch or finally clause must appear in a try statement. The finally clause will be executed in all cases, both in case of errors, in case of error-free execution of try part, and in cases where the control is passed out of try by means of a jumping command. We will now, in Program 36.14 study an example of a try-catch-finally statement.

Main of Program 36.14 arranges that M is called (in line 30) for each value in the enumeration type Control (line 5-6). Inside the static method M we illustrate a number of possible ways out of M:

  1. If reason is Returning , M calls return .
  2. If reason is Jumping , M calls goto which brings the control outside try-catch-finally.
  3. If reason is Continue , continue forces the for loop to the next iteration, which is non-existing. The call of continue leaves the try-catch-finally abruptly.
  4. If reason is Breaking , break breaks out of the for loop, and try-catch-finally is left abruptly.
  5. If reason is Throwing , an Exception is thrown. The exception is "handled" locally.
  6. If reason is 5, the expression (Control)i does not hit a value in the Control enumeration type. This causes execution and termination of the try clause, in particular the execution of WriteLine in line 17.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
using System;

class FinallyDemo{

  internal enum Control {Returning, Jumping, Continuing, Breaking,   
                         Throwing, Normal}                           

  public static void M(Control reason){
    for(int i = 1; i <= 1; i++)  // a single iteration               
      try{                                                           
        Console.WriteLine("\nEnter try: {0}", reason);
        if (reason == Control.Returning) return;
        else if (reason == Control.Jumping) goto finish;
        else if (reason == Control.Continuing) continue;
        else if (reason == Control.Breaking) break;
        else if (reason == Control.Throwing) throw new Exception();
        Console.WriteLine("Inside try");        
      }
      catch(Exception){                                              
        Console.WriteLine("Inside catch");                           
      }  
      finally{                                                       
        Console.WriteLine("Inside finally");                         
      }
    finish: return;
  }

  public static void Main(){
    for(int i = 0; i <= 5; i++)                                      
      M((Control)i);                                                 
  }
}
Program 36.14    Illustration of try-catch-finally.

The outcome of the example in Program 36.14 can be seen in the program output in Listing 36.15. So please take a careful look at it.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Enter try: Returning
Inside finally

Enter try: Jumping
Inside finally

Enter try: Continuing
Inside finally

Enter try: Breaking
Inside finally

Enter try: Throwing
Inside catch
Inside finally

Enter try: Normal
Inside try
Inside finally
Listing 36.15    Output from the try-catch-finally program.

As it appears, the finally clause is executed in each of the six cases enumerated above. Thus, it is not possible to bypass a finally clause of a try-catch-finally control structure. The finally clause is executed independent of the way we execute and leave the try clause.

You should place code in finally clauses of try-catch-finally or try-finally which should be executed in all cases, both in case or "normal execution", in case of errors, and in case of exit-attempts via jumping commands.

 

36.10.  Rethrowing an exception
Contents   Up Previous Next   Slide Annotated slide Aggregated slides    Subject index Program index Exercise index 

We will now study the idea of rethrowing an exception. We have already encountered and discussed exception rethrowing in Section 36.6 (see Program 36.4).

  • Rethrowing

    • Preserving information about the original exception, and the call chain

    • Usually recommended

In Program 36.16 we illustrate rethrowing by means of the standard example of this chapter. The situation is as follows:

  1. Main calls M , M calls N , N calls P .
  2. In P an IndexOutOfRangeException exception is thrown as usual.
  3. On its way back the calling chain, the exception is caught in N . But N regrets, and rethrows the exception.
  4. The exception is passed unhandled through M and Main .

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
using System;

class ExceptionDemo{    
                        
  public static void Main(){
    Console.WriteLine("Main");
    int[] table = new int[6]{10,11,12,13,14,15};
    int idx = 6;
    M(table, idx);      
  }

  public static void M(int[] table, int idx){
    Console.WriteLine("M(table,{0})", idx);   
    N(table,idx);
  }

  public static void N(int[] table, int idx){     
    Console.WriteLine("N(table,{0})", idx);       
    try{
      P(table,idx);                          
    }
    catch (IndexOutOfRangeException e){           
      // Will not/cannot handle exception here.   
      // Rethrow original exception.              
      throw;                                      
    }                                             
  }                                               

  public static void P(int[] table, int idx){
    Console.WriteLine("P(table,{0})", idx);
    Console.WriteLine("Accessing element {0}: {1}", 
                       idx, table[idx]);          
  }
}
Program 36.16    Rethrowing an exception.

The output of Program 36.16 is shown in Listing 36.17. From the stack trace in Listing 36.17 it does not appear that the static method N actually has touched (and "smelled to") the exception. This is a main point of this example.

1
2
3
4
5
6
7
8
9
10
11
12
Main
M(table,6)
N(table,6)
P(table,6)

Unhandled Exception: 
 System.IndexOutOfRangeException: 
  Index was outside the bounds of the array.
   at ExceptionDemo.P(Int32[] table, Int32 idx)
   at ExceptionDemo.N(Int32[] table, Int32 idx)
   at ExceptionDemo.M(Int32[] table, Int32 idx)
   at ExceptionDemo.Main()
Listing 36.17    Output from the program that rethrows an exception.

Touching, but not handling the exception

An outer handler will see the original exception

 

36.11.  Raising an exception in an exception handler
Contents   Up Previous Next   Slide Annotated slide Aggregated slides    Subject index Program index Exercise index 

We will now study an alternative to rethrowing, as discussed and illustrated in Program 36.16.

  • Raising and throwing a new exception

    • Use this approach if you, of some reason, want to hide the original exception

      • Security, simplicity, ...

    • Consider propagation of the inner exception

In Program 36.18 we show a program similar to the program discussed in the previous section. Instead of rethrowing the exception in N, we throw a new instance of IndexOutOfRangeException. As can be seen in Listing 36.19 this affects the stack trace. From the outside, we can no longer see that the problem occurred in P.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
using System;

class ExceptionDemo{

  public static void Main(){                          
    int[] table = new int[6]{10,11,12,13,14,15};
    int idx = 6;
    M(table, idx);
  }

  public static void M(int[] table, int idx){         
    Console.WriteLine("M(table,{0})", idx);
    N(table,idx);
  }

  public static void N(int[] table, int idx){         
    Console.WriteLine("N(table,{0})", idx);           
    try{
      P(table,idx);
    }
    catch (IndexOutOfRangeException e){                               
      // Will not/can no handle here. Raise new exception.            
      throw new IndexOutOfRangeException("Index out of range");       
    }                                                                 
  }

  public static void P(int[] table, int idx){
    Console.WriteLine("P(table,{0})", idx);
    Console.WriteLine("Accessing element {0}: {1}", 
                       idx, table[idx]);                        
  }
}
Program 36.18    Raising and throwing a new exception.

1
2
3
4
5
6
7
8
9
M(table,6)
N(table,6)
P(table,6)

Unhandled Exception: System.IndexOutOfRangeException: 
 Index out of range
   at ExceptionDemo.N(Int32[] table, Int32 idx)
   at ExceptionDemo.M(Int32[] table, Int32 idx)
   at ExceptionDemo.Main()
Listing 36.19    Output from the program that raises a new exception.

As a final option, we may wish to reflect, in relation to the client, that the problem actually occurred in P. This can be done by passing an inner exception to the new exception, as constructed in line 24 of Program 36.20. Notice the effect this has on the stack trace in Listing 36.21.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
using System;

class ExceptionDemo{

  public static void Main(){                            
    int[] table = new int[6]{10,11,12,13,14,15};
    int idx = 6;
    M(table, idx);
  }

  public static void M(int[] table, int idx){           
    Console.WriteLine("M(table,{0})", idx);
    N(table,idx);
  }

  public static void N(int[] table, int idx){           
    Console.WriteLine("N(table,{0})", idx);             
    try{
      P(table,idx);
    }
    catch (IndexOutOfRangeException e){                                      
      // Will not/cannot handle exception here.                              
      // Raise new exception with propagation of inner exception.            
      throw new IndexOutOfRangeException("Index out of range",  e);          
    }
  }

  public static void P(int[] table, int idx){
    Console.WriteLine("P(table,{0})", idx);
    Console.WriteLine("Accessing element {0}: {1}", 
                       idx, table[idx]);                                     
  }
}
Program 36.20    Raising and throwing a new exception, propagating original inner exception.

1
2
3
4
5
6
7
8
9
10
11
12
13
M(table,6)
N(table,6)
P(table,6)

Unhandled Exception: System.IndexOutOfRangeException: 
 Index out of range ---> System.IndexOutOfRangeException: 
  Index was outside the bounds of the array.
   at ExceptionDemo.P(Int32[] table, Int32 idx)
   at ExceptionDemo.N(Int32[] table, Int32 idx)
   --- End of inner exception stack trace ---
   at ExceptionDemo.N(Int32[] table, Int32 idx)
   at ExceptionDemo.M(Int32[] table, Int32 idx)
   at ExceptionDemo.Main()
Listing 36.21    Output from the program that raises a new exception, with inner exception.

 

36.12.  Recommendations about exception handling
Contents   Up Previous Next   Slide Annotated slide Aggregated slides    Subject index Program index Exercise index 

We are now almost done with exception handling. We will now formulate a few recommendations that are related to exception handling.

  • Control flow

    • Do not use throw and try-catch as iterative or conditional control structures

    • Normal control flow should be done with normal control structures

  • Efficiency

    • It is time consuming to throw an exception

    • It is more efficient to deal with the problem as a normal program aspect - if possible

  • Naming

    • Suffix names of exception classes with "Exception"

  • Exception class hierarchy

    • Your own exception classes should be subclasses of ApplicationException

    • Or alternatively (as of a more recent recommendation) of Exception.

  • Exception classes

    • Prefer predefined exception classes instead of programming your own exception classes

    • Consider specialization of existing and specific exception classes

  • Catching

    • Do not catch exceptions for which there is no cure

    • Leave such exceptions to earlier (outer) parts of the call-chain

  • Burying

    • Avoid empty handler exceptions - exception burrying

    • If you touch an exception without handling it, always rethrow it

 

36.13.  References
[Exceptions-best-practices]Best practices for handling exceptions (MSDN)
http://msdn.microsoft.com/en-us/library/seyhszts.aspx

Generated: Monday February 7, 2011, 12:19:10
Theme index -- Keyboard shortcut: 'u'  Previous theme in this lecture -- Keyboard shortcut: 'p'  Next slide in this lecture -- Keyboard shortcut: 'n'