Most .NET languages support structured exception handling.
Essentially, when an error occurs in your application, the .NET
Framework creates an exception object that represents the problem. You
can catch this object using an exception handler. If you fail to use an
exception handler, your code will be aborted, and the user will see an
error page.
Structured exception handling provides several key features:
Exceptions are object-based:
Each exception provides a significant amount of
diagnostic information wrapped into a neat object, instead of a simple
message and error code. These exception objects also support an
InnerException property that allows you to wrap a generic error over
the more specific error that caused it. You can even create and throw
your own exception objects.
Exceptions are caught based on their type:
This allows you to streamline error handling code without needing to sift through obscure error codes.
Exception handlers use a modern block structure:
This makes it easy to activate and deactivate
different error handlers for different sections of code and handle
their errors individually.
Exception handlers are multilayered:
You can easily layer exception handlers on top
of other exception handlers, some of which may check only for a
specialized set of errors.
Exceptions are a generic part of the .NET Framework:
This means they're completely cross-language
compatible. Thus, a .NET component written in C# can throw an exception
that you can catch in a web page written in VB.
NOTE
Exception handlers are a key programming
technique. They allow you to react to problems that occur at runtime
because of factors outside your control. However, you obviously
shouldn't use exception handlers to hide the bugs that might crop up in
your code! Instead, you need to track down these programmer mistakes at
development time and correct them. Visual Studio's debugging features can help you in this task.
7.2.1. The Exception Class
Every exception class derives from the base class
System.Exception. The .NET Framework is full of predefined exception
classes, such as NullReferenceException, IOException, SqlException, and
so on. The Exception class includes the essential functionality for
identifying any type of error. Table 1 lists its most important members.
Table 1. Exception Properties
Member | Description |
---|
HelpLink | A
link to a help document, which canbe a relative or fully qualified
uniform resource locator (URL) or uniform resource name (URN), such as
file:///C:/ACME/MyApp/help.html#Err42. The .NET Framework doesn't use
this property, but you can set it in your custom exceptions if you want
to use it in your web page code. |
InnerException | A
nested exception. For example, a method might catch a simple file
input/output (IO) error and create a higher-level "operation failed"
error. The details about the original error could be retained in the
InnerException property of the higher-level error. |
Message | A text description with a significant amount of information describing the problem. |
Source | The name of the application or object where the exception was raised. |
StackTrace | A
string that contains a list of all the current method calls on the
stack, in order of most to least recent. This is useful for determining
where the problem occurred. |
TargetSite | A
reflection object (an instance of the System.Reflection.MethodBase
class) that provides some information about the method where the error
occurred. This information includes generic method details such as the
method name and the data types for its parameter and return values. It
doesn't contain any information about the actual parameter values that
were used when the problem occurred. |
GetBaseException() | A
method useful for nested exceptions that may have more than one layer.
It retrieves the original (deepest nested) exception by moving to the
base of the InnerException chain. |
When you catch an exception in an ASP.NET page, it
won't be an instance of the generic System.Exception class. Instead, it
will be an object that represents a specific type of error. This object
will be based on one of the many classes that inherit from
System.Exception. These include diverse classes such as
DivideByZeroException, ArithmeticException, IOException,
SecurityException, and many more. Some of these classes provide
additional details about the error in additional properties.
Visual Studio provides a useful tool to browse through the exceptions in the .NET class library. Simply select Debug =>
Exceptions from the menu (you'll need to have a project open in order
for this to work). The Exceptions dialog box will appear. Expand the
Common Language Runtime Exceptions group, which shows a hierarchical
tree of .NET exceptions arranged by namespace (see Figure 1).
The Exceptions dialog box allows you to specify what
exceptions should be handled by your code when debugging and what
exceptions will cause Visual Studio to enter break mode immediately.
That means you don't need to disable your error handling code to
troubleshoot a problem. For example, you could choose to allow your
program to handle a common FileNotFoundException (which could be caused
by an invalid user selection) but instruct Visual Studio to pause
execution if an unexpected DivideByZero exception occurs.
To set this up, add a check mark in the Thrown
column next to the entry for the System.DivideByZero exception. This
way, you'll be alerted as soon as the problem occurs. If you don't add
a check mark to the Thrown column, your code will continue, run any
exception handlers it has defined, and try to deal with the problem.
You'll be notified only if an error occurs and no suitable exception
handler is available.
2. The Exception Chain
Figure 2
shows how the InnerException property works. In the specific scenario
shown here, a FileNotFoundException led to a NullReferenceException,
which led to a custom UpdateFailedException. Using an exception
handling block, the application can catch the UpdateFailedException. It
can then get more information about the source of the problem by
following the InnerException property to the NullReferenceException,
which in turn references the original FileNotFoundException.
The InnerException property is an extremely useful
tool for component-based programming. Generally, it's not much help if
a component reports a low-level problem such as a null reference or a
divide-by-zero error. Instead, it needs to communicate a more detailed
message about which operation failed and what input may have been
invalid. The calling code can then often correct the problem and retry
the operation.
On the other hand, sometimes you're
debugging a bug that lurks deep inside the component itself. In this
case, you need to know precisely what caused the error—you don't want
to replace it with a higher-level exception that could obscure the root
problem. Using an exception chain handles both these scenarios: you
receive as many linked exception objects as needed, which can specify
information from the least to the most specific error condition.