12.7. Exception PropertiesAs we discussed in Section 12.5, exception types derive from class Exception, which has several properties. These frequently are used in error messages indicating a caught exception. Two important properties are Message and StackTrace. Property Message stores the error message associated with an Exception object. This message can be a default message associated with the exception type or a customized message passed to an Exception object's constructor when the Exception object is thrown. Property StackTrace contains a (normally lengthy) String that represents the method-call stack. Recall that the runtime environment at all times keeps a list of open method calls that have been made but have not yet returned. The StackTrace represents the series of methods that have not finished processing at the time the exception occurs. Error-Prevention Tip 12.5
Property InnerExceptionAnother property used frequently by class-library programmers is InnerException. Typically, class library programmers "wrap" exception objects caught in their code so they then can throw new exception types that are specific to their libraries. For example, a programmer implementing an accounting system might have some account-number processing code in which account numbers are input as Strings but represented as Integers in the code. Recall that a program can convert Strings to Integer values with Convert.ToInt32, which throws a FormatException when it encounters an invalid number format. When an invalid account number format occurs, the accounting system programmer might wish to employ a different error message than the default message supplied by FormatException or might wish to indicate a new exception type, such as InvalidAccountNumberFormatException. In such cases, the programmer would provide code to catch the FormatException, then create an appropriate type of Exception object in the Catch block and pass the original exception as one of the constructor arguments. The original exception object becomes the InnerException of the new exception object. When an InvalidAccountNumberFormatException occurs in code that uses the accounting system library, the Catch block that catches the exception can obtain a reference to the original exception via property InnerException. Thus the exception indicates both that the user specified an invalid account number and that the problem was an invalid number format. If the InnerException property is Nothing, this indicates that the exception was not caused by another exception. Other Exception PropertiesClass Exception provides other properties, including HelpLink, Source and TargetSite. Property HelpLink specifies the location of the help file that describes the problem that occurred. This property is Nothing if no such file exists. Property Source specifies the name of the application where the exception occurred. Property TargetSite specifies the method where the exception originated. Demonstrating Exception Properties and Stack UnwindingOur next example (Fig. 12.5) demonstrates properties Message, StackTrace and InnerException, and method ToString, of class Exception. In addition, the example introduces stack unwindingwhen an exception is thrown but not caught in a particular scope, the method-call stack is "unwound," and an attempt is made to catch the exception in the next outer try block. We keep track of the methods on the call stack as we discuss property StackTrace and the stack-unwinding mechanism. To see the proper stack trace, you should execute this program using steps similar to those presented in Section 12.3. Figure 12.5. Exception properties and stack unwinding.
Program execution begins with Main, which becomes the first method on the method call stack. Line 9 of the try block in Main invokes Method1 (declared in lines 2931), which becomes the second method on the stack. If Method1 throws an exception, the Catch block in lines 1024 handles the exception and outputs information about the exception that occurred. Line 30 of Method1 invokes Method2 (lines 3436), which becomes the third method on the stack. Then line 35 of Method2 invokes Method3 (lines 3948), which becomes the fourth method on the stack. At this point, the method-call stack (from top to bottom) for the program is: Method3 Method2 Method1 Main The method called most recently (Method3) appears at the top of the stack; the first method called (Main) appears at the bottom. The try statement (lines 4147) in Method3 invokes Convert.ToInt32 (line 42), which attempts to convert a String to an Integer. At this point, Convert.ToInt32 becomes the fifth and final method on the call stack. Throwing an Exception with an InnerExceptionBecause the argument to Convert.ToInt32 is not in Integer format, line 42 throws a FormatException that is caught in line 43 of Method3. When the exception occurs, the call to Convert.ToInt32 terminates, so the method is removed (or unwound) from the method-call stack. The Catch block in Method3 then creates and throws an Exception object. The first argument to the Exception constructor is the custom error message for our example, "Exception occurred in Method3". The second argument is the InnerExceptionthe FormatException that was caught. The StackTrace for this new exception object reflects the point at which the exception was thrown (line 45). Now Method3 terminates, because the exception thrown in the Catch block is not caught in the method body. Thus, control returns to the statement that invoked Method3 in the prior method in the call stack (Method2). This removes, or unwinds, Method3 from the method-call stack. When control returns to line 35 in Method2, the CLR determines that line 35 is not in a try block. Therefore the exception cannot be caught in Method2, and Method2 terminates. This unwinds Method2 from the call stack and returns control to line 30 in Method1. Here again, line 30 is not in a try block, so Method1 cannot catch the exception. The method terminates and is unwound from the call stack, returning control to line 9 in Main, which is located in a try block. The try block in Main expires and the Catch block (lines 1024) catches the exception. The Catch block uses method ToString and properties Message, StackTrace and InnerException to create the output. Note that stack unwinding continues until a Catch block catches the exception or the program terminates. Displaying Information About the ExceptionThe first block of output (which we reformatted for readability) in Fig. 12.5 contains the exception's String representation, which is returned from method ToString. The String begins with the name of the exception class followed by the Message property value. The next four items present the stack trace of the InnerException object. The remainder of the block of output shows the StackTrace for the exception thrown in Method3. Note that the StackTrace represents 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 StackTrace line 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. Note that the inner-exception information includes the inner exception stack trace. Error-Prevention Tip 12.6
The next block of output (two lines) simply displays the Message property's value (Exception occurred in Method3) of the exception thrown in Method3. The third block of output displays the StackTrace property of the exception thrown in Method3. Note that this StackTrace property contains the stack trace starting from line 45 in Method3, because that is the point at which the Exception object was created and thrown. The stack trace always begins with the exception's throw point. Finally, the last block of output displays the String representation of the InnerException property, which includes the namespace and class name of the exception object, as well as its Message and StackTrace properties. |