Exception
PropertiesAs we discussed in Section 13.4, exception types derive from class Exception
, which has several properties. These frequently are used to formulate error messages indicating a caught exception. Two important properties are Message
and StackTrace
. Property Message
stores the string
error message associated with an Exception
object. This message can be a default message defined in the exception type or a customized message passed to an Exception
object’s constructor when the Exception
object is thrown. Property Stack-Trace
contains a 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. If the debugging information that’s generated by the compiler for the method is accessible to the IDE (e.g., the code is part of your project, rather than some third party library), the stack trace also includes line numbers; the first line number indicates the throw point, and subsequent line numbers indicate the locations from which the methods in the stack trace were called.
InnerException
Another frequently used property is InnerException
. When an exception occurs in a class library, it’s common for the library to catch that exception, then throw
a new one containing information that helps the client code programmer determine the exception’s cause. Class library programmers typically “wrap” the original exception object in the new exception object—this gives the client code programmer complete details of what led to the exception.
For example, a programmer implementing libraries used in an accounting system might have account-number processing code in which account numbers are input as
string
s but represented as int
s in the code. Recall that a program can convert string
s to int
values with int.Parse
, which throws a FormatException
if it encounters an invalid number format. When this happens, the library 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 InvalidAccountNumberException
.
In such cases, the library programmer would provide code to catch the FormatException
, then create an InvalidAccountNumberException
object in the catch
block, passing the original exception as a constructor argument. The original exception object becomes the InvalidAccountNumberException
object’s InnerException
. Section 13.8 shows how to create a custom exception class.
When an InvalidAccountNumberException
occurs in code that uses the accounting-system library, the catch
handler can reference the original exception via property Inner-Exception
. So the InvalidAccountNumberException
can indicate both that the user specified an invalid account number and that the number format was invalid. If the InnerException
property is null
, this indicates that the exception was not caused by another exception.
Exception
PropertiesClass Exception
provides other properties, including HelpLink
, Source
and TargetSite
:
Property HelpLink
specifies a link to the help file that describes the problem that occurred. This property is null
if no such file exists.
Property Source
specifies the name of the assembly (i.e., app or library) that caused the exception.
Property TargetSite
specifies the method where the exception originated.
Exception
Properties and Stack UnwindingOur next example (Fig. 13.5) demonstrates properties Message
, StackTrace
and Inner-Exception
of class Exception
. In addition, the example formally introduces stack unwinding—when 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 Stack-Trace
and the stack-unwinding mechanism. To see the proper stack trace, you should execute this program using steps similar to those presented in Section 13.2..
Program execution begins with Main
, which becomes the first method on the method-call stack. Line 14 of the try
block in Main
invokes Method1
(declared in lines 32–35), which becomes the second method on the stack. If Method1
throws an exception, the catch
block in lines 16–28 handles the exception and outputs information about the exception that occurred. Line 34 of Method1
invokes Method2
(lines 38–41), which becomes the third method on the stack. Then line 40 of Method2
invokes Method3
(lines 44–57), 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 47–56) in Method3
invokes method int.Parse
(line 49), which attempts to convert a string
to an int
. At this point, int.Parse
becomes the fifth and final method on the call stack.
Exception
with an InnerException
Because the argument to int.Parse
is not in int
format, line 49 throws a Format-Exception
that’s caught in line 51 of Method3
. The exception terminates the call to int.Parse
, so the method is unwound (i.e., removed) 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 InnerException
—the Format-Exception
that was caught. The StackTrace
for this new exception object reflects the point at which the exception was thrown (lines 54–55). 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 unwinds Method3
from the method-call stack.
When control returns to line 40 in Method2
, the CLR determines that line 40 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 34 in Method1
.
Here again, line 34 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 14 in Main
, which is located in a try
block. The try
block in Main
exits and the catch
block (lines 16–28) catches the exception. The catch
block uses properties Message
, Stack-Trace
and InnerException
to create the output. Stack unwinding continues until a catch
block catches the exception or the program terminates.
Exception
The first block of output (which we reformatted for readability) in Fig. 13.5 contains the exception’s string
representation, which is returned from an implicit call to method To-String
. 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 lines 54–55 of Method3
. 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. The inner-exception information includes the inner-exception stack trace.
When catching and rethrowing an exception, provide additional debugging information in the rethrown exception. To do so, create an object of an Exception
subclass containing more specific debugging information, then pass the original caught exception to the new exception object’s constructor to initialize the InnerException
property.
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
. This StackTrace
property contains the stack trace starting from line 54 in
Method3
, because that’s the point at which the Exception
object was created and thrown. The stack trace always begins from the exception’s throw point.
Finally, the last block of output displays the string
representation of the Inner-Exception
property, which includes the namespace and class name of the exception object, as well as its Message
and StackTrace
properties.