In this example, a class (AccessDatabase) is used to generate and manipulate Microsoft Access databases. Let's say this class has a static method called GenerateDatabase. Because the GenerateDatabase method would be used to create new Access databases, it would have to perform several tasks to create the database. For example, it would have to create the physical database file, create the specified tables (including any rows and columns your application needs), and define any necessary indexes and relations. The GenerateDatabase method might even have to create some default users and permissions.
The programmatic design problem is as follows: if an error were to occur in the CreateIndexes method, which method would handle it and how? Obviously, at some point, the method that originally called the GenerateDatabase method would have to handle the error, but how could it? It would have no idea how to handle an error that occurred several method calls deep in the code path. As we've seen, the calling method is said to not be in the correct context to handle the error. In other words, the only method that could logically create any meaningful error information about the error is the method that failed. Having said that, if return codes were used in our AccessDatabase class, each method in the code path would have to check for every single error code that every other method might return. One obvious problem with this is that the calling method would potentially have to handle a ridiculously large number of error codes. In addition, maintenance would be difficult. Every time an error condition was added to any of the methods in the code path, every other instance in the application where a method calls that method would have to be updated to handle the new error code. Needless to say, this is not an inexpensive proposition in terms of software total cost of ownership (TCO).-
Exception handling resolves all of these issues by enabling the calling method to trap for a given type of exception. In the example we're using, if a class called AccessDatabaseException were derived from Exception, it could be used for any types of errors that occur within any of the AccessDatabase methods. (I'll discuss the Exception class and deriving your own exception classes later in this chapter in "Using the System.Exception Class.") Then, if the CreateIndexes method failed, it would construct and throw an exception of type AccessDatabseException. The calling method would catch that exception and be able to inspect the Exception object to decipher what exactly went wrong. Therefore, instead of handling every possible type of return code that GenerateDatbase and any of its called methods could return, the calling method would be assured that if any of the methods in that code path failed, the proper error information would be returned. Exception handling provides an additional bonus: because the error information is contained within a class, new error conditions can be added and the calling method will remain unchanged. And wasn't extensibility-being able to build something and then add to it without changing or breaking existing code-one of the original promises of object-oriented programming to begin with? For these reasons, the concept of catching and dealing with errors in the correct context is the most significant advantage to using exception handling.