Up to this point, we have been catching exceptions, but we haven’t been doing anything with the exception object itself. As explained earlier, a catch clause allows you to specify an exception type and a parameter. The parameter receives the exception object. Since all exceptions are derived from Exception, all exceptions support the members defined by Exception. Here we will examine several of its most useful members and constructors, and put the catch parameter to good use.
Exception defines several properties. Three of the most interesting are Message, StackTrace, and TargetSite. All are read-only. Message contains a string that describes the nature of the error. StackTrace contains a string that contains the stack of calls that lead to the exception. TargetSite obtains an object that specifies the method that generated the exception.
Exception also defines several methods. One that you will often use is ToString( ), which returns a string that describes the exception. ToString( ) is automatically called when an exception is displayed via WriteLine( ), for example.
The following program demonstrates these properties and this method:
// Using Exception members. using System; class ExcTest { public static void genException() { int[] nums = new int[4]; Console.WriteLine("Before exception is generated."); // Generate an index out-of-bounds exception. for(int i=0; i < 10; i++) { nums[i] = i; Console.WriteLine("nums[{0}]: {1}", i, nums[i]); } Console.WriteLine("this won't be displayed"); } } class UseExcept { public static void Main() { try { ExcTest.genException(); } catch (IndexOutOfRangeException exc) { // catch the exception Console.WriteLine("Standard message is: "); Console.WriteLine(exc); // calls ToString() Console.WriteLine("Stack trace: " + exc.StackTrace); Console.WriteLine("Message: " + exc.Message); Console.WriteLine("TargetSite: " + exc.TargetSite); } Console.WriteLine("After catch statement."); } }
The output from this program is shown here:
Before exception is generated. nums[0]: 0 nums[1]: 1 nums[2]: 2 nums[3]: 3 Standard message is: System.IndexOutOfRangeException: Index was outside the bounds of the array. at ExcTest.genException() at UseExcept.Main() Stack trace: at ExcTest.genException() at UseExcept.Main() Message: Index was outside the bounds of the array. TargetSite: Void genException() After catch statement.
Exception defines four constructors. The two that are most commonly used are shown here:
Exception( ) Exception(string str)
The first is the default constructor. The second sets the Message property associated with the exception. When creating your own exception classes, you should implement both of these constructors.
The System namespace defines several standard, built-in exceptions. All are derived from SystemException since they are generated by the CLR when runtime errors occur. Several of the more commonly used standard exceptions defined by C# are shown in Table 13-1.
Exception | Meaning |
---|---|
ArrayTypeMismatchException | Type of value being stored is incompatible with the type of the array. |
DivideByZeroException | Division by zero attempted. |
IndexOutOfRangeException | Array index is out of bounds. |
InvalidCastException | A runtime cast is invalid. |
OutOfMemoryException | Insufficient free memory exists to continue program execution. For example, this exception will be thrown if there is not sufficient free memory to create an object via new. |
OverflowException | An arithmetic overflow occurred. |
NullReferenceException | An attempt was made to operate on a null reference—that is, a reference that does not refer to an object. |
Most of the exceptions in Table 13-1 are self-explanatory, with the possible exception of NullReferenceException. This exception is thrown when there is an attempt to use a null reference as if it referred to an object—for example, if you attempt to call a method on a null reference. A null reference is a reference that does not point to any object. One way to create a null reference is to explicitly assign it the value null by using the keyword null. Null references can also occur in other ways that are less obvious. Here is a program that demonstrates the NullReferenceException:
// Use the NullReferenceException. using System; class X { int x; public X(int a) { x = a; } public int add(X o) { return x + o.x; } } // Demonstrate NullReferenceException. class NREDemo { public static void Main() { X p = new X(10); X q = null; // q is explicitly assigned null int val; try { val = p.add(q); // this will lead to an exception } catch (NullReferenceException) { Console.WriteLine("NullReferenceException!"); Console.WriteLine("fixing...\n"); // now, fix it q = new X(9); val = p.add(q); } Console.WriteLine("val is {0}", val); } }
The output from the program is shown here:
NullReferenceException! fixing... val is 19
The program creates a class called X that defines a member called x and the add( ) method, which adds the invoking object’s x to the x in the object passed as a parameter. In Main( ), two X objects are created. The first, p, is initialized. The second, q, is not. Instead, it is explicitly assigned null. Then p.add( ) is called with q as an argument. Since q does not refer to any object, a NullReferenceException is generated when the attempt is made to obtain the value of q.x.
An interesting exception is StackOverflowException, which is thrown when the system stack is overrun. One situation in which this can happen is when a recursive method runs wild. A program that makes extensive use of recursion may want to watch for this exception, taking appropriate action if it occurs. Be careful, however. Since the system stack is exhausted when this exception is thrown, often the best action is to simply start returning from the recursive calls.