.NET is among the most robust software frameworks on the market today, yet with that awesome power, comes the responsibility of managing a huge variety of exceptions. Given that .NET enabled many different languages across all sorts of platforms and devices, .NET must be capable of handling errors that deal with all manner of issues, from web traffic and I/O to operating system problems and database exceptions. In fact, the latest version of .NET has built-in classes to handle over
300 unique exceptions in total!
Managing all these exceptions obviously presents a challenge, so Microsoft implemented a
namespace system within the .NET framework. A
namespace is basically just a unique name, which can be assigned to various objects or components, in order to more easily categorize and organize them.
In the case of the .NET framework,
namespaces are used to provide simple hierarchical structures to the thousands of classes that exist. For example, the
System namespace is the senior-most namespace in the entire framework: every other class and/or namespace in .NET is in some way derived from (a child of) the
Namespaces are then indicated by separating namespaces and classes with a series of chained periods (
.). For example, the
Object class is part of the
System namespace, so the full inheritance hierarchy is:
Whew! With the basics of how .NET uses
namespaces out of the way, we can dive into the exception class hierarchy. Since there are hundreds of exceptions in total and an overview of every possible exception namespace is pointless, we'll just examine a few fundamental exception namespaces and their relative hierarchy, as shown in the
Here's the basic list of .NET exception types, along with their base type that they inherit from:
|Exception type||Base type|
Below we'll examine each of these core exception types provided by .NET, looking at why and when they might be raised during normal execution.
The bread and butter of all .NET exceptions, as the name suggests, the
Exception class is the base class from which all other exceptions inherit. As with many exception handlers in other programming languages, the
Exception class provides a number of useful properties that assist with exception handling:
StackTrace: A stack trace to see where exactly the exception occurred.
InnerException: Useful when exceptions chain off one another. This allows one type of exception to throw another type of exception, ad infinitum.
Message: The detailed message indicating what happened.
Data: A dictionary that can be used to store arbitrary data associated with this particular exception instance.
SystemException houses all exceptions related to, well, the system. These include all runtime-generated errors, such as
System.IO.IOException, which is thrown when an I/O error occurs.
SystemException is the base class that all non-application errors inherit from. If your application code screws up somehow, that's handled by another exception type, but if the system screws up, it's on
SystemExceptionto sort it out.
IndexOutOfRangeException is thrown anytime an array or collection is accessed using an index outside its bounds.
If you attempt to use an object that is considered a
null object, the
NullReferenceException will be thrown.
AccessViolationException is thrown when
unmanaged code attempts to access memory which is unallocated. For many .NET applications, this will never occur, due to how .NET handles
Managed code is code that .NET compiles and executes using the
common language runtime. Conversely,
unmanagedcode compiles into machine code, which is not executed within the safety of the
Many languages that rely on .NET, such as
Visual Basic, are entirely compiled and executed in the
CLR. This means that code written in
C# is always
managed code and can, therefore, never throw an
However, a language like
Visual C++ does allow
unmanaged code to be written. In such cases, it's entirely possible to access unallocated memory, and throw an
InvalidOperationException is a pretty cool and somewhat intelligent exception. It is typically thrown when the state of an
object cannot support the particular method call being attempted for that object instance.
For example, when the
.MoveNext method is called on an enumerable object after elements in that collection have been modified. Since
MoveNext can no longer determine which object should be next up, it throws an
As the name implies,
ArgumentException is thrown when a call is made to a method using an invalid argument.
One interesting note is that since an executing program is not intelligent enough to deduce whether or not a provided argument is valid for the context of the current execution, most of the time an
ArgumentException is thrown, it's because a developer placed it there intentionally, to be thrown in a particular situation.
For example, if we create a
FullName(string first, string last) method, we're expecting a first and last name to be passed as arguments, both of which are strings. However, we may also have other requirements, like neither argument may contain any unexpected punctuation. We can purposely add logic into our code that validates the arguments that were passed, and if we determine the
first name provided is invalid, we intentionally throw an
ArgumentException. By doing so, this ensures that we (or any other developer that uses our method) know if we're trying to pass invalid arguments to our method.
ArgumentNullException is inherited from
ArgumentException, but is thrown specifically when a method is called that doesn't allow an argument to be
Another child of
ArgumentOutOfRangeException error is thrown when a method expects argument values within a specified range, yet the provided argument falls outside those bounds.
ExternalException is the base exception used for any error occurring externally, outside the bounds of your own application. Typically, this means that it's used when there's a problem communicating with a web address, database, and the like.
ComException requires a bit of history, so please bear with me. Prior to the creation of the .NET framework, way back in 1993, Microsoft created and begin using the
Component Object Model (
COM) binary-interface, which effectively allowed code to be used across languages and environments, in an object-oriented fashion.
COM handled exceptions through a property called
HResult (or result handler), which is a 32-bit value that combines a few fields together into a simple string. The encoded
HResult would tell the application which exception was thrown and the application could respond appropriately.
About a decade after
COM was introduced, .NET was first released and it quickly began taking over as a much more powerful programming framework, and thus, the usage of
COM quickly died out.
COM, .NET is far more complex and primarily uses direct object-oriented handling of
Exception classes for all exception management. However, in order to provide backward compatibility, .NET provides access to
Exception.HResult property. Each exception in .NET is mapped to a distinct
HResult value. When
managed code throws an exception, the
common language runtime finds the appropriate exception class based on the
HResult property, allowing
COM objects to deal with normal, useful .NET exception classes rather than convoluted
In cases where .NET sees an
HResult that it's unfamiliar with, a
COMException is thrown.
Also inherited from
SEHException acts as a sort of "catch-all" for
unmanaged code exceptions which have not already been mapped to an existing .NET exception class.
SEH, in this instance, stands for
structured exception handling, which is a mechanism used in .NET for handling both hardware and software exceptions.
SEHException requires an exception from
unmanaged code that has not already been mapped to an existing exception type which might better suit it, throwing of an
SEHException is rather rare.
To get the most out of your own applications and to fully manage any and all .NET Exceptions, check out the Airbrake .NET Bug Handler, offering real-time alerts and instantaneous insight into what went wrong with your .NET code, along with built-in support for a variety of popular development integrations including: JIRA, GitHub, Bitbucket, and much more.