You have an exception hierarchy that consists of a base exception class and multiple derived exception classes. At some point in your code, you want to handle only one or two of these derived exceptions in a specific manner. All other derived exceptions should be handled in a more generic manner. You need a clean way to target specific exceptions in an exception class hierarchy to be handled differently from the rest.
The exception handlers for C#
allow for multiple catch
clauses to be
implemented. Each of these catch
clauses can take
a single parameter—a type derived from the
Exception
class. An exception handler that uses
multiple catch clauses is shown here:
try { int d = 0; int z = 1/d; } catch(DivideByZeroException dbze) { Console.WriteLine("A divide by zero exception occurred. Error message == " + dbze.Message); } catch(OverflowException ofe) { Console.WriteLine("An Overflow occurred. Error message == " + ofe.Message); } catch(Exception e) { Console.WriteLine("Another type of error occurred. Error message == " + e.Message); }
This code produces the following output:
A divide by zero exception occurred. Error message == Attempted to divide by zero.
Notice the exception types that each catch
clause
handles in this try-catch
block. These specific
exception types will be handled on an individual basis within their
own catch
block. Suppose the
try
block looked as follows:
try { int z2 = 9999999; checked{z2 *= 999999999;} }
We would get the following message:
An Overflow occurred. Error message == Arithmetic operation resulted in an overflow.
Now, since the OverflowException
is being thrown,
it is handled in a totally different catch
block.
You may be thinking that you could do the same thing in a single
catch
block using an if-else
statement. An example of this is shown here:
catch(Exception e) { if (e is OverflowException) Console.WriteLine("An Overflow occurred. Error message == " + e.Message); else if (e is DivideByZeroException) Console.WriteLine("A divide by zero exception occurred. Error message == " + e.Message); else Console.WriteLine("Another type of error occurred. Error message == " + e.Message); }
The if-else
statements are used to check the type
of this exception and then execute the appropriate code. This
structure has two flaws. The first is that the compiler does not
check whether the exceptions are listed in the correct order in the
if-else
statements. If an exception class is
placed in the if-else
conditional structure after
a class in which it inherits from, the derived class will never be
checked. Consider the following modified catch
clause:
try { int d = 0; int z = 1/d; } catch(Exception e) { if (e is ArithmeticException) Console.WriteLine("The base class exception was chosen."); else if (e is OverflowException) Console.WriteLine("An Overflow occurred. Error message == " + e.Message); else if (e is DivideByZeroException) Console.WriteLine("A divide by zero exception occurred. Error message == " + e.Message); else Console.WriteLine("Another type of error occurred. Error message == " + e.Message); }
This code produces the following output:
The base class exception was chosen.
Even though the DivideByZeroException
was thrown,
the ArithmeticException
is always found first, as
the DivideByZeroException
and
OverflowException
both have the
ArithmeticException
class as their base class.
The second flaw is one of appearance. Using multiple
catch
clauses is much easier to read due to its
natural and consistent structure. This is the way the language should
be used, and, therefore, this is what many developers are going to
look for. Other developers reading your code may find it more natural
to read the multiple catch
classes rather than the
single catch
clause with a decision structure
inside of it. Not everyone may agree with us on this part, but we do
consider structure and consistency an integral part of writing good
code.
There is one case where we would consider using the single
catch
clause with the decision structure: when
large amounts of code would have to be duplicated in each
catch
clause and there is no way to put the
duplicated code in a finally
clause after the
try-catch
block.