2. When the app executes, another compiler (known as the just-in-time compiler
13.8 User-Defined Exception Classes
When control returns to line 40 inMethod2, the CLR determines that line 40 is not in atryblock. Therefore the exception cannot be caught inMethod2, andMethod2termi- nates. ThisunwindsMethod2from the call stack and returns control to line 34 inMethod1. Here again, line 34 is not in atryblock, soMethod1cannot catch the exception. The method terminates and isunwound from the call stack, returning control to line 14 in
Main, whichislocated in atryblock. Thetryblock inMainexits and thecatchblock (lines 16–28) catches the exception. Thecatchblock uses properties Message, Stack-
TraceandInnerExceptionto create the output. Stack unwinding continues until acatch block catches the exception or the program terminates.
Displaying Information About theException
The first block of output (which we reformatted for readability) in Fig. 13.5 contains the exception’sstringrepresentation, which is returned from animplicitcall to methodTo-
String. Thestringbegins with the name of the exception class followed by theMessage property value. The next four items present the stack trace of theInnerExceptionobject.
The remainder of the block of output shows theStackTracefor the exception thrown in
Method3. TheStackTracerepresents the state of the method-call stack at the throw point of the exception, rather than at the point where the exception eventually is caught. Each
StackTraceline that begins with “at” represents a method on the call stack. These lines indicate the method in which the exception occurred, the file in which the method resides and the line number of the throw point in the file. The inner-exception information in- cludes theinner-exception stack trace.
The next block of output (two lines) simply displays theMessage property’s value (Exception occurred in Method3) of the exception thrown inMethod3.
The third block of output displays theStackTraceproperty of the exception thrown inMethod3. ThisStackTraceproperty contains the stack trace starting from line 54 in
Method3, because that’s the point at which theExceptionobject was created and thrown.
The stack tracealwaysbegins from the exception’s throw point.
Finally, the last block of output displays thestring representation of the Inner-
Exception property, which includes the namespace and class name of the exception object, as well as itsMessageandStackTraceproperties.
13.8 User-Defined Exception Classes
In many cases, you can use existing exception classes from the .NET Framework Class Li- brary to indicate exceptions that occur in your programs. In some cases, however, you might wish to create new exception classes specific to the problems that occur in your programs.
User-defined exception classesshould derive directly or indirectly from classExceptionof namespaceSystem. When you create code that throws exceptions, they should be well doc- umented, so that other developers who use your code will know how to handle them.
Error-Prevention Tip 13.4
When catching and rethrowing an exception, provide additional debugging information in the rethrown exception. To do so, create anExceptionobject containing more specific debugging information, then pass the original caught exception to the new exception ob- ject’s constructor to initialize theInnerExceptionproperty.
ClassNegativeNumberException
Figures 13.6–13.7 demonstrate a user-defined exception class.NegativeNumberException
(Fig. 13.6) represents exceptions that occur when a program performs an illegal operation on a negative number, such as attempting to calculate its square root.
According to Microsoft’s document on “Best Practices for Handling Exceptions”
(bit.ly/ExceptionsBestPractices), user-defined exceptions should typically extend classException, have a class name that ends with “Exception” and define three construc-
Good Programming Practice 13.1
Associating each type of malfunction with an appropriately named exception class im- proves program clarity.
Software Engineering Observation 13.3
Before creating a user-defined exception class, investigate the existing exceptions in the .NET Framework Class Library to determine whether an appropriate exception type already exists.
1 // Fig. 13.6: NegativeNumberException.cs
2 // NegativeNumberException represents exceptions caused by 3 // illegal operations performed on negative numbers.
4 using System;
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 } // end class NegativeNumberException
Fig. 13.6 | NegativeNumberExceptionrepresents exceptions caused by illegal operations performed on negative numbers.
class NegativeNumberException : Exception // default constructor
public NegativeNumberException()
: base( "Illegal operation for a negative number" ) {
// empty body
} // end default constructor
// constructor for customizing error message
public NegativeNumberException( string messageValue ) : base( messageValue )
{
// empty body
} // end one-argument constructor
// constructor for customizing the exception's error // message and specifying the InnerException object public NegativeNumberException( string messageValue,
Exception inner )
: base( messageValue, inner ) {
// empty body
} // end two-argument constructor
13.8 User-Defined Exception Classes 511
tors: aparameterless constructor; aconstructor that receives astringargument(the error mes- sage); and aconstructor that receives astringargument and anExceptionargument(the error message and the inner-exception object). Defining these three constructors makes your exception class more flexible, allowing other programmers to easily use and extend it.
NegativeNumberExceptions most frequently occur during arithmetic operations, so it seems logical to derive classNegativeNumberExceptionfrom classArithmeticExcep- tion. However, classArithmeticException derives from class SystemException—the category of exceptions thrown by the CLR. Per Microsoft’s best practices for exception handling,user-defined exception classes should inherit fromExceptionrather thanSystem-
Exception. In this case, we could have used the built-inArgumentExceptionclass, which is recommended in the best practices for invalid argument values. We create our own exception type here simply for demonstration purposes.
ClassNegativeNumberException
ClassSquareRootTest(Fig. 13.7) demonstrates our user-defined exception class. The app enables the user to input a numeric value, then invokes methodSquareRoot(lines 40–48) to calculate the square root of that value. To perform this calculation,SquareRootinvokes classMath’sSqrtmethod, which receives adoublevalue as its argument. Normally, if the argument isnegative, methodSqrtreturnsNaN. In this program, we’d like topreventthe user from calculating the square root of a negative number. If the numeric value that the user enters is negative, method SquareRoot throws aNegativeNumberException (lines 44–45). Otherwise,SquareRootinvokes classMath’s methodSqrtto compute the square root (line 47).
When the user inputs a value, thetry statement (lines 14–34) attempts to invoke
SquareRootusing the value input by the user. If the user input is not a number, aFormat-
Exceptionoccurs, and thecatchblock in lines 25–29 processes the exception. If the user inputs a negative number, methodSquareRootthrows aNegativeNumberException(lines 44–45); thecatchblock in lines 30–34 catches and handles this type of exception.
1 // Fig. 13.7: SquareRootTest.cs
2 // Demonstrating a user-defined exception class.
3 using System;
4
5 class SquareRootTest 6 {
7 static void Main( string[] args )
8 {
9 bool continueLoop = true; 10
11 do
12 {
13 // catch any NegativeNumberException thrown
14 try
15 {
16 Console.Write(
17 "Enter a value to calculate the square root of: " );
18 double inputValue = Convert.ToDouble( Console.ReadLine() );
Fig. 13.7 | Demonstrating a user-defined exception class. (Part 1 of 2.)
19 double result = SquareRoot( inputValue );
20
21 Console.WriteLine( "The square root of {0} is {1:F6}\n",
22 inputValue, result );
23 continueLoop = false;
24 } // end try
25 catch ( FormatException formatException )
26 {
27 Console.WriteLine( "\n" + formatException.Message );
28 Console.WriteLine( "Please enter a double value.\n" );
29 } // end catch
30 catch ( NegativeNumberException negativeNumberException )
31 {
32 Console.WriteLine( "\n" + negativeNumberException.Message );
33 Console.WriteLine( "Please enter a non-negative value.\n" );
34 } // end catch
35 } while ( continueLoop );
36 } // end Main 37
38 // computes square root of parameter; throws
39 // NegativeNumberException if parameter is negative 40 public static double SquareRoot( double value )
41 {
42 // if negative operand, throw NegativeNumberException 43 if ( value < 0 )
44 45
46 else
47 return Math.Sqrt( value ); // compute square root 48 } // end method SquareRoot
49 } // end class SquareRootTest
Enter a value to calculate the square root of: 30 The square root of 30 is 5.477226
Enter a value to calculate the square root of: hello Input string was not in a correct format.
Please enter a double value.
Enter a value to calculate the square root of: 25 The square root of 25 is 5.000000
Enter a value to calculate the square root of: -2 Square root of negative number not permitted Please enter a non-negative value.
Enter a value to calculate the square root of: 2 The square root of 2 is 1.414214
Fig. 13.7 | Demonstrating a user-defined exception class. (Part 2 of 2.)
throw new NegativeNumberException(
"Square root of negative number not permitted" );