400 likes | 543 Vues
Exceptions Version 1.0. Objectives. At the conclusion of this lesson, students should be able to Explain the need for exceptions Correctly write programs that use exceptions Explain the rules for exception handling and exception . Motivation.
E N D
Objectives At the conclusion of this lesson, students should be able to Explain the need for exceptions Correctly write programs that use exceptions Explain the rules for exception handling and exception
Motivation Consider the following case. A programmer writes a program, using a library function fnc( ). Method fnc( ) encounters a situation that it cannot handle. The method fnc( ) is capable of detecting the situation, but does not know anything about the program in which it was imbedded, so does not know how to handle it. What should the method do?
The method has several choices: It can terminate the program. This could be very bad in a program that cannot afford to crash. It can leave the state of the data used in the program in a complete mess, or otherwise cause serious harm. Rule of thumb: Methods should not terminate the program!
It can return an error value. Two problems occur with this approach. 1. The program may already return a valid data value, and a valid value does not exist to signal an error. 2. If an error code is returned, then the checking of return values at all levels of the program becomes very tedious. This is common behavior in C programs, where there is no built-in exception handling. It can create “spaghetti” code.
It can return some value, but leave the program in an error state. This may be the worst situation, since the user does not know that an error occurred. Meanwhile, the program has done something wrong and no one knows.
The C# exception mechanism is meant to handle exceptional situations … that is, situations where some part of the program could not do what it was supposed to do. The important idea in exception handling is that the code that discovers the exception condition is not (necessarily) responsible for handling the exception. It can push the exception up the through the sequence of callers until it reaches a point where there is sufficient information about the context in which the program is running to resolve the problem.
Example main ( ) start a dialogue with end user * get some data * get a file name call a method to do some calculations with the data This method, if it knew that the filename was wrong, could ask the user for a different name. user interface code open a file and save the result of the calculations calculation code This method knows nothing about files or filenames file manager code This method does not know that any calculations were done or where the file name came from.
// GetValueAt method static intGetValueAt(int[] array, int index) { return array[index]; } This method can figure out if the index is out of bounds, but it has no idea about where the array or the index came from, so the best that it can do is report an error if the index is out of bounds. It is unable to do any error recovery.
Suppose Main( ) contains this code … What happens when the user enters an index of 5? const int SIZE = 3; int[] data = new int[SIZE]; // fill the array for (int i = 0; i < SIZE; i++) { Console.Write("Enter a value: "); data[i] = int.Parse(Console.ReadLine()); } // get data at an index provided by the user, quit if index is -1 int value; do { Console.Write("Enter an index or -1 to quit: "); value = int.Parse(Console.ReadLine()); if (value != -1) Console.WriteLine("The value at this index is {0}", GetValueAt(data, value)); } while (value != -1);
We will do several things to fix this problem. 1. We will put a test in the GetValueAt( )method to test the index. If it is out of range, we will throw an exception. 3. In Main( ), we will put the call to the GetValueAt( ) method in a try block. 4. We will write a catch block in Main( ) to handle the exception.
static intGetValueAt(int[] array, int index) { if (index > array.Length) throw (new Exception("array index out of bounds") ); return array[index]; } If index is greater than the Length property of the array then we would get an out of bounds exception. But inside this method, we don’t have enough context to solve the problem, so create an object of the Exceptionclass, and pass it back to the calling method. When an exception is thrown, execution of the method stops immediately and control returns to the calling point with the exception on the stack.
if (value != -1) { try { intarrayValue = GetValueAt(data, value); Console.WriteLine("The value at this index is {0}", arrayValue); } catch (Exception e) { Console.WriteLine(e.Message); } } Place any code where you expect that an exception may occur inside of a try block. Also include in the try block any statements you want skipped, should an exception occur. If no exception occurs, control proceeds to the first statement after the catch block.
if (value != -1) { try { intarrayValue = GetValueAt(data, value); Console.WriteLine("The value at this index is {0}", arrayValue); } catch (Exception e) { Console.WriteLine(e.Message); } } The catch block must immediately follow the try block. If an exception of the type given asa parameter is received by the calling method, then this catch block is executed. Otherwise, it is not. Control always goes to the statement after the catch block.
The Exception Class In C#, the exception handling mechanism only allows objects of the Exception class and its derived classes to be thrown or caught. The Exception class has two important properties: * Message – a string containing an error message * StackTrace – a string representing the method calling sequence that generated the exception
The Message Property static intGetValueAt(int[] array, int index) { if (index > array.Length) throw (new Exception("array index out of bounds") ); return array[index]; } Pass a message to the Exception constructor
The Message Property catch (Exception e) { Console.WriteLine(e.Message); } Displaying the message property
The StackTrace Property catch (Exception e) { Console.WriteLine(e.StackTrace); }
The exception was thrown here at Program.GetValueAt(Int32[ ] array, Int32 index) in C:\Documents\badArray\Program.cs:line 68 at Program.Main( ) in C:\Documents\badArray\ Program.cs:line 50 The GetValueAt method was called here
Built In Exception Classes When possible, use the Exception classes that are built into C#’s .net framework. These all are derived from the base class -- Exception. The on-line documentation for each method in the .Net framework explains what exceptions that method will throw. Or … look at the debug window you get when an uncaught exception occurs.
Multiple Exceptions When a method executes, it may be possible that more than one kind of an exception may occur. C# provides a mechanism for the calling method to figure out which exception occurred.
Multiple things can go wrong inside • this try block • The user may enter an invalid integer • The user may enter a value that is outside • of the boundaries of the array try { Console.Write("Enter an index or -1 to quit: "); value = int.Parse(Console.ReadLine()); if (value != -1) { intarrayValue = GetValueAt(data, value); Console.WriteLine("The value at this index is {0:D}", arrayValue); } }
So we need three catch blocks, one right after the other. Each takes as its parameter the type of exception it will handle or an empty param list. catch(IndexOutOfRangeException e) { Console.WriteLine("Index is out of Range, please try again."); } catch(FormatException e) { Console.WriteLine("You did not enter an integer, please try again."); } catch { Console.WriteLine(“Unknown error, please try again."); } Only the catch block matching the type of exception thrown will be executed. The other catch block(s) will be skipped. The empty catch will catch any error.
If an exception is not caught, your program will terminate and the stack trace will be displayed.
Catch Rules The catch block that is executed is the first catch block following the currently active try block, whose parameter matches the type thrown. The default catch catch will catch any exception type thrown. If no match is found, the program is terminated.
Re-throwing an exception Suppose that a method catches an exception, but the catch block cannot completely handle the error. It can re-throw the exception, causing it to flow to the code that called this method. throw;
Exception Propagation Sometimes called stack unwinding, exception propagation works as follows: If an exception is not handled where it occurs, All of the local variables in the called function are destroyed The metrhodimmediately exits and the uncaught exception is raised in (passed to) the calling method. Control returns to the point where the call was made If the call was inside of a try block, an attempt is made to catch the exception - If a matching catch block is found, it is executed - If no match is found, control passes to the calling method, using this same mechanism. If the calling code is not inside of a try block, control passes to the calling method, using this same mechanism.
void Main ( ) { try { Method1 ( ); Console.WriteLine(“It worked!!”); } catch(Exception e ) { Console.WriteLine(“Exception … didn’t work!”); Console.WriteLine(e.Message); } Console.ReadLine( ); }
void Method11 ( ) { Console.WriteLine(“Starting Method 1 … hit a key”; Console.ReadLine( ); Console.WriteLine(“Calling Method2“); Method2( ); Console.WriteLIne(“you won’t see this message!”); }
void Method2 ( ) { Console.WriteLine(“Starting Method2 … hit a key”); Console.ReadLine( ); Console.WriteLine(“Calling Method3“); Method3 ( ); Console.WriteLIne(“you won’t see this message!”); }
void Method3 ( ) { Console.WriteLine(“Starting Method3 … press a key”); Console.ReadLine( ); Console.WriteLine(“Whoa baby”); throw (new Exception(“Thrown in method 3”); }
Stack Main ( ) { try { method1( ); } catch { } . . . void method1 ( ) { method2 ( ) … rtn address2 rtn address1 exception rtn addressm void method2 ( ) { method3 ( ) … Now find the right catch block! exception void method3 ( ) { throw exception; . . . exception
Resource Leaks Suppose that your program is reading from a file and an exception occurs. You handle the exception and the program continues without ever closing the file. This is considered a resource leak because your program is no longer using the file, and other programs cannot use the file because it is open in this one. You should always close a file when you are done with it. Whether an exception occurs or not, you have to make that the file gets closed.
try { open a file . . . reading the file . . . close the file when done } catch( ) { handle the exception } If there is an exception the file does not get closed.
try { open a file . . . reading the file . . . } catch { handle the exception close the file } If there is no exception the file does not get closed.
try { open a file . . . reading the file . . . close the file } catch { handle the exception close the file } Duplicate code is error prone
try { open a file . . . reading the file . . . } catch { handle the exception } finally { close the file } Now the file gets closed whether or not there is an exception.