Exception handling

Exception handling is the black art of doing something to repair an unpredicted error or malfunction. Within CLR, anytime something happens outside our prevision, such as setting an Int16 typed variable with a value outside valid ranges, the CLR will handle such an event by itself, creating an instance of an Exception class and breaking the execution of our code, trying instead to find some other code able to handle (a.k.a catch) such an exception.

Any Exception class is populated with all useful details regarding what just happened, like a simplified error text (within the Message property), the StackTrace that explains exactly the whole method call hierarchy, and other details. Often, instead of a simple Exception class, an inheritance child is instantiated to collect specific additional details or simply to define the kind of exception just raised. Indeed, setting an outranged value within an Int16 typed variable will raise an OverflowException event in place of a simple Exception event.

As just said, an exception is usually handled within a .NET-based application in response to an unpredicted error, although an exception is actually a special GoTo statement that will alter the control-flow of our application. This is the why its called as an exception instead of an error.

Anytime an error happens, or simply when the flow cannot proceed as normal, an exception is created and raised (raising an exception actually starts the control-flow alteration) to avoid completing a method run, maybe, because its data is inconsistent. We can also create our exceptions regarding our business, components, or helpers/frameworks if special parameters are needed to flow.

Carefully create and handle exceptions because of the cost the CLR incurs when any exception is raised. Although an exception will start a control-flow alteration, at the beginning, CLR will compute the full call stack of the executing thread. This is a CPU-intensive operation that actually stops the thread from running any other code. This is proof that Microsoft considers the entire exception-handling framework as error management. Therefore, it is impossible to create a multiple control-flow application using exception handling without enabling great wastage of resources. This means that, if we still need to be creating multiple control-flow applications, we will still need to use the goto keyword.

Here is a graphical representation that shows the control-alteration made by any exception:

Exception handling

A flow diagram of an exception with its control-flow in search of a continuation

As seen in the preceding diagram, when the control-flow changes, the CLR searches for some catch code-block. This may be locally, or at any calling level, up to the program's Main method. Here is a classical implementation in C#:

int a = 0;

try
{
    //normal control-flow
    a = 10;
    a = a / int.Parse("0"); //this will raise a DivideByZeroException
}
catch (DivideByZeroException dx)
{
    //altered control-flow if CLR raises DivideByZeroException
    Console.WriteLine(dx.Message);
}
catch (Exception ex)
{
    //altered control-flow if CLR raises any other exception
    Console.WriteLine(ex.Message);
}
finally
{
    //usually used for cleanup resources
    //or restore data state

    a = 0;
}

The CLR executes the code within the try block; then, when an exception is raised, CLR searches the best-fit altered control-flow by matching handled exceptions (with the catch keyword) with the exception raised. The search is from top to bottom and supports class hierarchy. This means that the less specific exception (the one handled with the generic Exception class) must be always the last one. Otherwise, other exceptions will never be matched.

In the preceding example, there is a catch block for such an exact exception raised, so the flow will continue in that block. After any try or catch block, the finally block is invoked, if present (optional).

A try-catch block can be nested in others if needed, although this may lead to a control flow that is tricky to understand with all those alterations. By nesting exceptions, CLR still goes in search of a catch block from the deeper code row where the exception has originated, flowing up to the process' Main method. Such exceptions lift, like an air bubble in the water, it lets programmers work with exception-handing search as made by CLR from the deeper code to the most outer one (the Main method), the name of exception bubbling.

Raising new exceptions is actually simple; it is enough to use the throw keyword and pass the new exception:

throw new Exception("HI");

Any time an exception is raised somewhere, the related AppPool is notified of the FirstChanceException event that actually runs before the bubbling occurs—in other words, at the start of the bubbling.

During the bubbling, if the CLR cannot find any valid catch block, the related AppDomain (the one where the exception originated) is notified on the UnhandledException event. Although this cannot handle the exception as a super catch block can, it can somehow notify application users or the system administrator gracefully before the critical exit of the process. Here is an such an example:

static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
    //contains exception details
    var ex = e.ExceptionObject;

    //if true the process will terminate
    var willKillCLR = e.IsTerminating;
}

static void CurrentDomain_FirstChanceException(object sender, FirstChanceExceptionEventArgs e)
{
    //contains exception details
    var ex = e.Exception;
}

After the UnhandledException event occurs, the AppDomain class is unloaded by CLR.

A special case occurs when another compiler raises a non Common Language Specification (CLS) compatible exception (such as raising a string exception or int exception—classes that do not inherit from the Exception class). Although this is a rare opportunity, some external vendor language implementation could work with such behavior. In this case, the CLR will raise a RuntimeWrappedException event with the ability to read raw exception data, such as an int value or a string value, as an internal exception.

Another special case to be aware of is that a finally block—although it is usually called, anything that can happen within a catch block—cannot run if the executing thread is killed by unmanaged code, by invoking the Win32 KillThread or KillProcess method.

Obviously, in such cases, Windows will also kill the whole process with anything within. The only leak that will still survive will occur when you launch an external process driven by your application that somehow was killed by Windows. In this case, the finally block is avoided and the external process can remain in memory. A widely-used solution is to always check if zombie external processes are still alive from previous executions of the application, when we start a new instance of such an application.

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

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