Exception
HierarchyIn C#, the exception-handling mechanism allows only objects of class Exception
(namespace System
) and its derived classes to be thrown and caught. Note, however, that C# programs may interact with software components written in other .NET languages (such as C++) that do not restrict exception types. The general catch
clause can be used to catch such exceptions.
This section overviews several of the .NET Framework’s exception classes and focuses exclusively on exceptions that derive from class Exception
. In addition, we discuss how to determine whether a particular method throws exceptions.
SystemException
Class Exception
(namespace System
) is the base class of .NET’s exception class hierarchy. An important derived class is SystemException
. The CLR generates SystemException
s. Many of these can be avoided if apps are coded properly. For example, if a program attempts to access an out-of-range array index, the CLR throws an exception of type Index-OutOfRangeException
(a derived class of SystemException
). Similarly, an exception occurs when a program uses a reference-type variable to call a method when the reference has a value of null
. This causes a NullReferenceException
(another derived class of SystemException
). You saw earlier in this chapter that a DivideByZeroException
occurs in integer division when a program attempts to divide by zero.
Other exceptions thrown by the CLR include OutOfMemoryException
, StackOverflowException
and ExecutionEngineException
, which are thrown when something goes wrong that causes the CLR to become unstable. Sometimes such exceptions cannot even be caught. It’s best to simply log such exceptions (using a tool such as Apache’s log4net—http:/
), then terminate your app.
A benefit of the exception class hierarchy is that a catch
block can catch exceptions of a particular type or—because of the is-a relationship of inheritance—can use a base-class type to catch exceptions in a hierarchy of related exception types. For example, Section 13.3.2 discussed the catch
block with no parameter, which catches exceptions of all types (including those that are not derived from Exception
). A catch
block that specifies a parameter of type Exception
can catch all exceptions that derive from Exception
, because Exception
is the base class of all exception classes in the .NET Framework. The advantage of this approach is that the exception handler can access the caught exception’s information via the parameter in the catch
. We’ll say more about accessing exception information in Section 13.7..
Using inheritance with exceptions enables a catch
block to catch related exceptions using a concise notation. A set of exception handlers could catch each derived-class exception type individually, but catching the base-class exception type is more concise. However, this technique makes sense only if the handling behavior is the same for a base class and all derived classes. Otherwise, catch each derived-class exception individually.
The compiler issues an error if a catch
block that catches a base-class exception is placed before a catch
block for any of that class’s derived-class types. In this case, the base-class catch
block would catch all base-class and derived-class exceptions, so the derived-class exception handler would never execute.
How do we determine that an exception might occur in a program? For methods contained in the .NET Framework classes, read the detailed descriptions of the methods in the online documentation. If a method may throw an exception, its description contains a section called Exceptions that specifies the types of exceptions the method may throw and briefly describes what causes them. For an example, search for “int.Parse
method” in the Visual Studio online documentation. The Exceptions section of this method’s web page indicates that method int.Parse
throws three exception types:
ArgumentNullException
,
FormatException
, and
OverflowException
and describes the reasons for each. [Note: You also can find this information in the Object Browser described in Section 10.11..]
If a method may throw exceptions, statements that invoke the method directly or indirectly should be placed in try
blocks, and those exceptions should be caught and handled.
It’s more difficult to determine when the CLR may throw exceptions. Such information appears in the C# Language Specification, which specifies cases in which exceptions are thrown. At time of writing, the C# specification has not yet been officially released by Microsoft. You can view an unofficial copy at:
https://github.com/ljw1004/csharpspec/blob/gh-pages/README.md