By now I hope that you are starting to think very hard about how programs fail. You should also be thinking that when you build a system you should consider how you are going to manage the way that it will fail. This is actually a very important part of the design process. When something bad happens the program must deal with this in a managed way. The key to achieving this is to think about making your own custom exceptions for your system.
We have been on the receiving end of exceptions already. We have seen that if something bad happens whilst a file is being read, or a Parse method is given an invalid string, the system will throw an exception to indicate that it is unhappy. This means that potentially badly behaved code like this has to be enclosed in a try – catch construction so that our program can respond sensibly. Now we are going to take a closer look at exceptions and see about creating our own exception types.
Advanced Programming Structured Error Handling
5.7.1 The Exception class
The C# System namespace holds a large number of different exceptions, but they are all based on the parent Exception class. If you want to throw your own exceptions you are strongly advised to create your own exception type which extends the system one. You can generate System.Exception when something goes wrong, but this means that exceptions produced by your code will get mixed up with those produced by other parts of the system. If you want your code to be able to explicitly handle your errors the best way forward is to create one or more of your own exceptions.
This means that, along with everything else in your system, you will need to design how your program will handle and generate errors. This all (of course) leads back to the metadata which you have gathered, in that the specification will give you information about how things can fail and the way that the errors are produced.
5.7.2 Creating your own exception type
Creating your exception type is very easy. It can be done by simply extending the System.Exception class:
public class BankException : System.Exception {
}
This works because the System.Exception class has a default constructor which takes no parameters. The default constructor in BankException (which is added automatically by the compiler), can just use this to make an Exception instance.
However, there is a version of the Exception constructor which lets us add a message to the exception. This can be picked up and used to discover what has gone wrong. To create a standard exception with a message I pass the constructor a string. If I want to use this with my bank exception I have to sort out the constructor chaining for my exception class and write code as follows:
public class BankException : System.Exception {
public BankException (string message) : base (message)
{ } }
This makes use of the base keyword to call the constructor of the parent class and pass it the string message.
5.7.3 Throwing an Exception
The throw keyword throws an exception at that point in the code. The thing that is thrown must be based on the System.Exception class. For example, I might want to throw an exception if the name of my account holder is an empty string. I can do this with the code:
if ( inName.Length == 0 ) {
throw new BankException( "Invalid Name" );
}
The throw keyword is followed by a reference to the exception to be thrown. In the code above I make a new instance of the bank exception and then throw that. At this point the execution would transfer to the ―nearest‖ catch construction. This might be a catch of mine, or it might be one supplied by the system. If the exception is caught by the system it means that my program will be terminated. However, I can catch the exception myself as follows:
Advanced Programming Structured Error Handling
Account a;
try {
a = new Account(newName, newAddress);
}
catch (BankException exception) {
Console.WriteLine( "Error : " + exception.Message);
}
The code tries to create a new account. If doing this causes an exception to be thrown, the code in the exception handler is obeyed. The reference exception is set to refer to the exception which has been thrown. The Message member of an exception is the text which was given. This means that if I try to create an account with an empty name the exception will be thrown, the catch invoked, and the message printed.
Programmer’s Point: Design your error exceptions yourself
An exception is an object which describes how things have gone wrong. It can contain a text message which you can display to explain the problem to the user. However, you should seriously consider extending the exception class to make error exceptions of your own which are even more informative. Errors should be numbered, i.e. your exceptions should be tagged with an error number. This helps a great deal in working with different languages. If the customer can say “Error number 25” when they are reporting a problem it makes it much easier for the support person to respond sensibly. Note that, as with just about everything else, you need to design in your handling of errors.
5.7.4 Multiple Exception Types
It is worth spending time thinking about how the exceptions and errors in your system are to be managed. We can have many catch constructions if we like, and the catch will be matched up to the type of exception that was thrown:
public class BankExceptionBadName : System.Exception {
public BankExceptionBadName (string message) : base (message)
{ } }
public class BankExceptionBadAddress : System.Exception {
public BankExceptionBadAddress (string message) : base (message)
{ } }
Now I can use different catches, depending on how the code fails:
Advanced Programming Program Organisation
Account a;
try {
a = new Account("Rob", "");
}
catch (BankExceptionBadName nameException) {
Console.WriteLine("Invalid name : " + nameException.Message);
}
catch (BankExceptionBadAddress addrException) {
Console.WriteLine("Invalid address : " + addrException.Message);
}
catch (System.Exception exception ) {
Console.WriteLine("System exception : " + exception.Message);
}
Each of the catches matches a different type of exception. At the very end of the list of handlers I have one which catches the system exception. This will be called if the exception is not a name or an address one.
Programmer’s Point: Programs often fail in the error handlers
If you think about it, error handlers are rather hard to test. They only get run when something bad happens, and most of the time you will be using test data which assumes everything is OK. This means that errors are more likely to get left in the error handlers themselves, since the code doesn’t get exercised as much as the rest of the system. Of course this is a recipe for really big disasters, in that the error handler is supposed to put things right and if it fails it will usually make a bad situation much worse.
As a professional programmer you must make sure that your error handling code is tested at least as hard as the rest of the system. This might mean that you have to create special test versions of the system to force errors into the code.
Believe me, it is worth the effort!
Error handling should be something you design in. When you create the system you decide how many and what kind of errors you are going to have to manage.