Creating Dedicated catch Statements

So far, you’ve been working with generic catch statements only. You can create dedicated catch statements that handle only some exceptions and not others, based on the type of exception thrown. Example 16-4 illustrates how to specify which exception you’d like to handle. This example performs some simple division. As you’d expect, dividing by zero is illegal, and C# has a specific exception just for that. For the purposes of demonstration, we’ll say that the dividend in the operation also cannot be zero. Mathematically, that’s perfectly legal, but we’ll assume that a result of zero would cause problems elsewhere in the program. Obviously, C# doesn’t have an exception type for that, so we’ll use a more general exception for that case.

Example 16-4. Each of these three dedicated catch statements is intended for a different type of exception

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Example_16_4_ _ _ _Dedicated_catch_Statements
{
    class Tester
    {

        public void Run()
        {
            try
            {
                double a = 5;
                double b = 7;
                Console.WriteLine("Dividing {0} by {1}...", a, b);
                Console.WriteLine("{0} / {1} = {2}", a, b,
                                  DoDivide(a, b));
            }

            // most specific exception type first
            catch (DivideByZeroException)
            {
                Console.WriteLine("DivideByZeroException caught!");
            }

            catch (ArithmeticException)
            {
                Console.WriteLine("ArithmeticException caught!");
            }

            // generic exception type last
            catch
            {
                Console.WriteLine("Unknown exception caught");
            }
        }

        // do the division if legal
        public double DoDivide(double a, double b)
        {
            if (b == 0)
            {
                throw new DivideByZeroException();
            }
            if (a == 0)
            {
                throw new ArithmeticException();
            }
            return a / b;
        }

        static void Main()
        {
            Console.WriteLine("Enter Main...");
            Tester t = new Tester();
            t.Run();
            Console.WriteLine("Exit Main...");
        }
    }
}

In Example 16-4, the DoDivide() method does not let you divide zero by another number, nor does it let you divide a number by zero. If you try to divide by zero, it throws an instance of DivideByZeroException. As we mentioned, we’ll also assume you don’t want to allow division of zero by any number; in that case, you will throw an ArithmeticException.

When the exception is thrown, the runtime examines each exception handler in the order in which it appears in the code and matches the first one it can. If you were to run this program with a=5 and b=7, the output would be:

5 / 7 = 0.7142857142857143

As you’d expect, no exception is thrown. However, when you change the value of a to 0, the output is:

ArithmeticException caught!

The exception is thrown, and the runtime examines the first exception: DivideByZeroException. Because this does not match, it goes on to the next handler, ArithmeticException, which does match.

In a final pass through, suppose you change a to 7 and b to 0. This throws the DivideByZeroException.

You have to be particularly careful with the order of the catch statements in this case because the DivideByZeroException is derived from ArithmeticException. If you reverse the catch statements, the DivideByZeroException matches the ArithmeticException handler and the exception never gets to the DivideByZeroException handler. In fact, if their order is reversed, it is impossible for any exception to reach the DivideByZeroException handler. Then the compiler recognizes that the DivideByZeroException handler cannot be reached and reports a compile error!

Typically, a method catches every exception it can anticipate for the code it is running. However, it is possible to distribute your try/catch statements, catching some specific exceptions in one function and more generic exceptions in higher calling functions. Your design goals should dictate exactly where you put each try and catch statement.

Assume you have a method A that calls another method B, which in turn calls method C, which calls method D, which then calls method E. Method E is deep in your code, and methods B and A are higher up. If you anticipate that method E might throw an exception, you should create a try/catch block deep in your code to catch that exception as close as possible to the place where the problem arises—but only if there’s sensible action to take at the level of method E. Many programmers will put an exception handler at the top of their program (or at the top of each module) to handle unanticipated exceptions that would otherwise “slip by” and trigger the built-in exception handler. This at least allows your program to fail gracefully (and for some programs, to log an error, or notify someone by email). It often turns out that these “last chance” exception handlers in early versions of the application lead to the addition of one or two more specific exception handlers in future versions.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset