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.

33.  Fundamental Questions about Exception Handling

With this chapter we start the lecture about exception handling. We could as well just use the word "error handling". Before we approach object-oriented exception handling we will in this chapter discuss error handling broadly and from several perspectives. In Chapter 34 we will discuss non-OO, conventional exception handling. In Chapter 35 we encounter object-oriented exception handling. Finally, in Chapter 36 we discuss exception handling in C#. Chapter 36 is the main chapter in the lecture about exception handling.

33.1 What is the motivation?33.4 When are errors detected?
33.2 What is an error?33.5 How are errors handled?
33.3 What is normal? What is exceptional?33.6 Where are errors handled?

33.1.  What is the motivation?
Contents   Up Previous Next   Slide Annotated slide Aggregated slides    Subject index Program index Exercise index 

The following items summarize why we should care about error handling:

  • Understand the nature of errors

    • "An error is not just an error"

  • Prevent as many errors as possible in the final program

    • Automatically - via tools

    • Manually - in a distinguished testing effort

  • Make programs more robust

    • A program should, while executing, be able to resist and survive unexpected situations


33.2.  What is an error?
Contents   Up Previous Next   Slide Annotated slide Aggregated slides    Subject index Program index Exercise index 

The word "error" is often used in an undifferentiated way. We will now distinguish between errors in the development process, errors in the source program, and errors in the executing program.

  • Errors in the design/implementation process

    • Due to a wrong decision at an early point in time - a mental flaw

  • Errors in the source program

    • Illegal use of the programming language

    • Erroneous implementation of an algorithm

  • Errors in the program execution - run time errors

    • Exceptions - followed by potential handling of the exceptions

Errors in the development process may lead to errors in the source program.

Errors in the source program may lead to errors in the running program


33.3.  What is normal? What is exceptional?
Contents   Up Previous Next   Slide Annotated slide Aggregated slides    Subject index Program index Exercise index 

I propose that we distinguish between "normal aspects" and "exceptional aspects" when we write a program. Without this distinction, many real-world programs will become unwieldy. The separation between normal aspects and exceptional aspects adds yet another dimension of structure to our programs.

In many applications and libraries, the programming of the normal aspects leads to nice and well-proportional solution. When exceptional aspects (error handling) are brought in, the normal program aspects are polluted with error handling code. In some situations the normal program aspects are totally dominated by exceptional program aspects. This is exemplified in Section 34.2.

Below we characterize the normal program aspects and the exceptional program aspects.

  • Normal program aspects

    • Situations anticipated and dealt with in the conventional program flow

    • Programmed with use of selective and iterative control structures

  • Exceptional program aspects

    • Situations anticipated, but not dealt with "in normal ways" by the programmer

      • Leads to an exception

      • Recoverable via exception handling. Or non-recoverable

    • Situations not anticipated by the programmer

      • Leads to an exception

      • Recoverable via exception handling. Or non-recoverable

    • Problems beyond the control of the program

Let us assume that we program the following simple factorial function. Recall that "n factorial" = Factorial(n) = n! = n * (n -1) * ... * 1.

    public static long Factorial(int n){
      if (n == 0)
        return 1;
      else return n * Factorial(n - 1);

The following problems may appear when we run the Factorial function:

  1. Negative input : If n is negative an infinite recursion will appear. It results in a StackOverflowException .
  2. Wrong type of input : In principle we could pass a string or a boolean to the function. In reality, the compiler will prevent us from running such a program, however.
  3. Wrong type of multiplication : The operator my be redefined or overloaded to non-multiplication.
  4. Numeric overflow of returned numbers : The type long cannot contain the result of 20! , but not 21! .
  5. Memory problem : We may run out of RAM memory during the computation.
  6. Loss of power : The power may be interrupted during the computation.
  7. Machine failure : The computer may fail during the computation.
  8. Sun failure : The Sun may be extinguished during the computation.

Problem 1 should be dealt with as a normal program aspects. As mentioned, the problem in item 2 is prevented by the analysis of the compiler. Problem 3 is, in a similar way, prevented by the compiler. Problem 4 is classified as an anticipated exceptional aspect. Problem 4 could, alternatively, be dealt with by use of another type than long, such a BigInteger which allows us to work with arbitrary large integers. (BigInteger is not part of the .Net 3.5 libraries, however). Problem 5 could also be foreseen as an anticipated exception. Problem 5, 7, and 8 are beyond the control of the program. In extremely critical applications it may, however, be considered to deal with (handle) problem 6 and 7.

Let us, briefly, address the numeric overflow problem relative to C#. Per default, numeric overflow in integer types does not lead to exceptions in C#. (The result of the evaluation "wraps around", a gives wrong results). It is, however, possible to embed a piece of program in a checked{...} form, in which case numeric overflow leads to exceptions. It is also possible to ask for another default handling of numeric overflow by use of the compiler option /checked+.

With use of normal control structures, a different (although a hypothetical) type BigInteger, and an iterative instead of a recursive algorithm we may rewrite the program to the following version:

    public static BigInteger Factorial(int n){
      if (n >= 0){
        BigInteger res = 1;
        for(int i = 1; i <= n; i++)
          res = res * i;
        return res;
      else throw new ArgumentException("n must be non-negative");

With this rewrite we have dealt with problem 1, 4, and 5. As an attractive alternative to the if-else, problem 1 could be dealt with by the precondition n >= 0 of the Factorial method, see Section 50.1.

As it appears, we wish to distinguish between normal program aspects and exceptional program aspects via the programming language mechanisms used to deal with them. In C# and similar object-oriented languages, we have special means of expressions to deal with exceptions. The Factorial function shown above throws an exception in case of negative input. See Section 36.2 for details.

Above, we distinguish between different degrees of exceptional aspects. As a programmer, you are probably aware of something that can go wrong in your program. Other errors come as surprises. Some error situations, both the expected and the surprising ones, should be dealt with such that the program execution survives. Others will lead to program termination. Controlled program termination, which allows for smooth program restart, will be an important theme in this lecture.


33.4.  When are errors detected?
Contents   Up Previous Next   Slide Annotated slide Aggregated slides    Subject index Program index Exercise index 

It is attractive to find errors as early as possible

Our next question cares about the point in time where you - the program developer - realize the problem. It should be obvious that we wish to identify troubles as soon as possible.

We identify the following error identification times.

  • During design and programming - Go for it.

  • During compilation - syntax errors or type errors - Attractive.

  • During testing - A lot of hard work. But necessary.

  • During execution and final use of the program

    • Handled errors - OK. But difficult.

    • Unhandled errors - A lot of frustration.

If we are clever enough, we will design and program our software such that errors do not occur at all. However, all experience shows that this is not an easy endeavor. Still, it is good wisdom to care about errors and exception handling early in the development process. Problems that can be dealt with effectively at an early point in time will save a lot of time and frustrations in the latter phases of the development process.

Static analysis of the program source files, as done by the front-end of the compiler, is important and effective for relatively early detection of errors. The more errors that can be detected by the compiler before program execution, the better. Handling of errors caught by the compiler requires very little work from the programmers. This is at least the case if we compare it with testing efforts, described next.

Systematic test deals with sample execution of carefully prepared program fragments. The purpose of testing is to identify errors (see also Section 54.1). Testing activities are very time consuming, but all experience indicates that it is necessary. We devote a lecture, covered by Chapter 56 in this material, to testing. We will in particular focus on unit test of object-oriented programs.

Software test is also known as validation in relation to the specification of the software. Alternatively, the program may be formally verified up against a specification. This goes in the direction of a mathematical proof, and an area known as model-checking.

Finally, some errors may creep through to the end-use of the program. Some of these errors could and should perhaps have been dealt with at an earlier point in time. But there will remain some errors in this category. Some can be handled and therefore hidden behind the scene. A fair amount cannot be handled. Most of the discussion in this and the following three chapters are about (handled and unhandled) errors that show up in the program at execution time.


33.5.  How are errors handled?
Contents   Up Previous Next   Slide Annotated slide Aggregated slides    Subject index Program index Exercise index 

Assuming that we now know about the nature of errors and when they appear in the running program, it is interesting to discuss what to do about them. Here follows some possibilities.

  • Ignore

    • False alarm - Naive

  • Report

    • Write a message on the screen or in a log - Helpful for subsequent correction of the source program

  • Terminate

    • Stop the program execution in a controlled an gentle way - Save data, close connections

  • Repair

    • Recover from the error in the running program - Continue normal program execution when the problem is solved

The first option - false alarm - is of course naive and unacceptable from a professional point of view. It is naive in the sense that shortly after we have ignored the error another error will most certainly occur. And what should then be done?

The next option is to tell the end-user about the error. This is naive, almost in the same way as false alarm. But the reporting option is a very common reaction from the programmer: "If something goes wrong, just print a message on standard output, and hopefully the problem will vanish." At least, the user will be aware that something inappropriate has happened.

The termination option is often the most viable approach, typically in combination with proper reporting. The philosophy behind this approach is that errors should be corrected when they appear. The sooner the better. The program termination should be controlled and gentle, such that it is possible to continue work when the problem has been solved. Data should be saved, and connections should be closed. It is bad enough that a program fails "today". It is even worse if it is impossible start the program "tomorrow" because of corrupted data.

Repair and recovery at run-time is the ultimate approach. We all wish to use robust and stable software. Unfortunately, there are some problems that are very difficult to deal with by the running program. To mention a few, just think of broken network connections, full harddisks, and power failures. It is only in the most critical applications (medical, atomic energy, etc) that such severe problems are dealt with explicitly in the software that we construct. Needless to say, it is very costly to built software that takes such problems into account.


33.6.  Where are errors handled?
Contents   Up Previous Next   Slide Annotated slide Aggregated slides    Subject index Program index Exercise index 

The last fundamental question is about the place in the program where to handle errors. Should we go for local error handling, or for handling at a more remote place in the program.

  • Handle errors at the place in the program where they occur

    • If possible, this is the easiest approach

    • Not always possible nor appropriate

  • Handle errors at another place

    • Along the calling chain

    • Separation of concerns

If many errors are handled in the immediate proximity of the source of the error, chances are that a small and understandable program becomes large, unwieldy, and difficult understand. Separation of concerns is worth considering. One concern is the normal program aspects (see Section 33.3). Another concern is exception handling. The two concerns may be dealt with in different corners or the program. Propagation of errors from one place in a C# program to another will be discussed in Section 36.7 of this material.

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