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. |
Before we approach exception handling in object-oriented programs we will briefly take a look at some conventional ways to deal with errors. You can, for instance, think of these as error handling techniques in C programming.
|
34.1. Exception Handling Approaches
Contents Up Previous Next Slide Annotated slide Aggregated slides Subject index Program index Exercise index
One way to deal with errors is to bring the error condition to the attention of the user of the program. (See Section 33.5 ). Obviously, this is done in the hope that the user has a chance to react on the information he or she receives. Not all users can do so.
If error messages are printed to streams (files) in conventional, text based user interfaces, it is typical to direct the information to the standard error stream instead of the standard output stream.
|
We identify the following conventional exception handling approaches:
|
When a function is used in imperative C programming, the value returned by the function can be used to signal an error condition to its caller. Many functions from the standard C library signal errors via the value returned from the function. Unfortunately, varying conventions are applied. In some functions, such as main, a non-zero value communicates a problem to the operating environment. In functions that return pointers (such as malloc) a NULL value typically indicates an error condition. In other functions, such as fgetc and fputc, the distinguished EOF (end of file) value is used as an error value. Other functions, such as mktime, use -1 as an error value.
As an supplementing means, a global variable can be used for signaling more details about an error. In C programming, the variable errno from the standard library errno.h is often used. When a function returns an error value (as discussed above), the value of errno is set to a value that gives more details about the error. Some systems use errno as an index to an array of error messages.
Use of error return values and global error variables is not a good solution to the error handling problem. Therefore, most contemporary languages come with more sophisticated facilities for raising, propagating and handling exceptions. The C# error handling facilities are covered specifically in Section 36.2, Section 36.3, and Section 36.7.
34.2. Mixing normal and exceptional cases
Contents Up Previous Next Slide Annotated slide Aggregated slides Subject index Program index Exercise index
Before we enter the area of object-oriented exception handling, and exception handling in C#, we will illustrate the danger of mixing "normal program aspects" and "exceptional program aspects". See also the discussion in Section 33.3.
In Program 34.1 a small program, which copies a file into another file, is organized in the Main method in a C# program. The string array passed to Main is supposed to hold the names of the source and target files. Most readers will probably agree that the program fragment shown in Program 34.1 is relatively clear and straightforward.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | using System; using System.IO; public class CopyApp { public static void Main(string[] args) { FileInfo inFile = new FileInfo(args[0]), outFile = new FileInfo(args[1]); FileStream inStr = inFile.OpenRead(), outStr = outFile.OpenWrite(); int c; do{ c = inStr.ReadByte(); if(c != -1) outStr.WriteByte((byte)c); } while (c != -1); inStr.Close(); outStr.Close(); } } | |||
|
We will now care about possible issues that can go wrong in our file copy program. The result can be seen in Program 34.2, where all red aspects are oriented towards error handling. Some of the error handling issues are quite realistic. Others may be slightly exaggerated with the purpose of making our points.
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 50 51 52 53 54 55 56 | using System; using System.IO; public class CopyApp { public static void Main(string[] args) { FileInfo inFile; do { inFile = new FileInfo(args[0]); if (!inFile.Exists) args[0] = "some other input file name"; } while (!inFile.Exists); FileInfo outFile; do { outFile = new FileInfo(args[1]); if (outFile.Exists) args[1] = "some other output file name"; } while (outFile.Exists); FileStream inStr = inFile.OpenRead(), outStr = outFile.OpenWrite(); int c; do{ c = inStr.ReadByte(); if(c != -1) outStr.WriteByte((byte)c); if (StreamFull(outStr)) DreamCommand("Fix some extra room on the disk"); } while (c != -1); inStr.Close(); if (!FileClosed(inStr)) DreamCommand("Deal with input file which cannot be closed"); outStr.Close(); if (!FileClosed(outStr)) DreamCommand("Deal with output file which cannot be closed"); } /* Programming pseudo commands for the sake of this example */ public static void DreamCommand(string str){ // Carry out the command str } public static bool FileClosed(Stream str){ // Return if the stream str is closed } public static bool StreamFull(Stream str){ // Return if the stream str is full } } | |||
|
The line intervals 8-12 and 15-19 deal with non-existing/existing input/output files respectively. It is a problem if the input file is non-existing, and it may be problematic to overwrite an existing output file. Line 27-28 deals with memory problems, in case there is not enough room for the output file. The line intervals 32-33 and 36-37 address file closing problems. The methods DreamCommand, FileClosed, and StreamFull are imaginary abstractions, which are needed to complete and compile the version of the file copy program shown in Program 34.2.
The important lesson to learn from the example above is that the original "normal file copying aspects" in Program 34.1 almost disappears in between the error handling aspects of Program 34.2.