Deep C# - Take Exception to Everything
Written by Mike James   
Thursday, 12 March 2020
Article Index
Deep C# - Take Exception to Everything
Catch Clauses
What is an Exception
The State Problem
ThreadException handling

What is an exception?

Most exceptions are generated by the CLR, i.e. the runtime. In many ways the whole idea of an exception is to provide a way for the runtime to tell the application that something is wrong.

You can think of the archetypal exception as being either hardware generated - a disk error - or one stage removed from being a hardware problem - shortage of memory. The whole point is that these are external conditions or errors which have nothing much to do with the logic of your program. Why should you build allowances for hardware faults into your program logic?

It is in this sense that an exception is exceptional.

What has happened in practice is that this idea has drifted into a weaker idea of "things that don't happen very often".

For example, is a divide by zero error something exceptional or should your program logic expect it, test for it and deal with it?

You could just as easily add an if statement in front of the division as surround the code with a try catch. On the other hand you would be hard pressed to consider a statement such as:

if(!file error) {}

or any condition that mentioned the hardware state, as reasonably part of your program logic - but we do regard it as reasonably as part of an exception handling routine.

So despite the idea that exceptions are a way for the lower level hardware and software to communicate to our application any difficulties they might be having exceptions have become the norm as a way of signalling almost any sort of error. As a result the framework and third party classes raise exceptions and so can any application you care to write.

To give you some idea of how arbitrary the division between error, exception and perfectly normal behaviour is - consider the checked keyword. When you are doing integer arithmetic the chances are good that you will generate an overflow at some point.

Is this an exception?

If you surround the arithmetic by checked() then an overflow does raise an exception but surrounding it by unchecked() ignores any overflow.

Notice that checking for arithmetic overflow using prior if statements would be a difficult task, so in this case the distinction is between perfectly normal code and an exception.

Notice also  that the reason why overflow isn't always an exception is that low level algorithms often use integer arithmetic to do arithmetic modulo a power of 2. This is a very grey area and perhaps a high level language really shouldn't reflect what goes on under the bonnet in quite this way. The good news is that by default overflow produces an exception which is how it should be.

The checked keyword gives us the best illustration of the distinction between an error and an exception:

  • An error is something that you could easily check for
  • An exception is something that is difficult to check for before the instruction that actually fails.

Notice that this definition depends on what facilities the high level language gives you to check for error conditions.

For example, all we have to do to convert numeric overflow from an exception to an error is provide an IsTooBig predicate.

Banner

Custom exceptions

To raise an exception all you have to do is define a custom exception type or use one of the existing types and simply use throw exception type.

For example:

throw new NullReferenceException();

You can also use throw within a catch block to pass the exception on to a higher level handler - if there is one. It is always worth being clear that throwing an exception from within a handler means that you are giving up on the exception and allowing some other part of the system deal with it. The possible consequence of this is that it isn't handled and a runtime error occurs.

For example:

catch (DivideByZeroException myException)
{
MessageBox.Show(
"division by zero isn't allowed " +
myException.Source);
throw myException;
}

passes the exception on to some other handler.

Of course if you want to throw a completely customised exception you need to create your own type that derives from another exception class. You also have to do the work in constructing an object of the type. It's accepted practice to end all exception classes with Exception so, for example, if you wanted to raise a custom exception "TooLongException",  in case a task is taking too long you would define something like:

class TooLongException : Exception
{
public TooLongException(){}
public TooLongException(
string message):base(message){}
public TooLongException(string message,
Exception inner):base(message,inner){}
}

Exceptions usually have three constructors but you don't have to follow this rule. The default constructor simply returns a new object of the type and allows the exception to be raised simply as:

throw new TooLongException();

The second constructor allows a message to be included when the exception is raised:

throw new TooLongException(
"much much too long");

and finally the third constructor that allows a thrown exception to be passed on as part of the exception:

NullReferenceException innercause=
new NullReferenceException();
throw new TooLongException(
"much much too long", innercause);

Of course in a real example the inner exception would have been generated elsewhere and passed up to the catch handler that is throwing the TooLong exception. This allows the next handler to determine why the TooLong exception might have occurred.

 

Considering the building of a custom exception brings us to the subject of what information should be packaged into an exception object.

There are some standard properties that are inherited from Exception but you can add as many extras as seem appropriate. The most commonly used are:

  • the message property to pass on an error message to the user
  • Helplink which is the URL of a helpfile
  • InnerException which stores any exception passed to the exception
  • Source and TargetSite which give the object and function that caused the exception.

More complicated is the HResult property which is used to return the standard Windows error code if the exception was caused by an API.

The most complicated piece of information that an exception object contains is the stack trace - a string containing a list of what method called what to get you to the location where the exception occurred.  You can add to the properties and methods of the exception class but most programmers don't and there are some good reasons why not.

Banner



Last Updated ( Thursday, 12 March 2020 )