Before I start the chapter proper, I think it's worth discussing a subtle distinction in terminology. Exceptions and errors aren't synonymous; in other words, they're different creatures . Exceptions can be used to signal errors (such as division by zero), unexpected events (for instance, the printer running out of paper), and violations of an interface's assumptions (for example, passing a parameter with a negative value when the interface expects a positive value). Although each of these three exceptions could be called an error from the point of view of the method that throws the exception, it's the developer writing the code calling the method who really knows what he or she expects and who therefore defines which of these exceptions is actually an error from the point of view of the application as a whole.
For example, a method that performs a printing task might throw an exception because the printer is out of paper, but if the code that calls that method has been programmed correctly, this would be an expected event, not an error at all. The exception would just be passed up the call stack before being transformed into a message asking the user to place some more paper in the printer.
This distinction is important because when you design a method or class, you need to decide how you're going to communicate information to the calling method and which exceptions you're going to throw. Say that you've been given the task of designing and implementing a class that validates a user login to an application. In the method that validates the user login, should an exception be thrown when the login fails or should the method return a value of false instead?
There is no correct answer to this question. Some developers maintain that a login failure is an error and therefore an exception should be thrown. Other developers reason that a login failure is an expected event and not really an error as far as the application is concerned , therefore the method should return a status variable rather than forcing the calling code to handle an exception. Although in this situation I favor returning a status variable and not throwing an exception, you and your team will have to decide for yourselves how you are going to handle these design decisions.
One point to bear in mind when making these types of decisions is that you, as a designer of a method or class, often don't know what the code that calls your method considers to be an error. If, for instance, you're developing a method that reads information from a file on disk, how should you signal that the calling method has read past the end of the file? Is reading past the end of the file an error or not? Because you don't know what the calling code is doing, you just don't know whether this is an error. In this situation, I would throw an exception because I just can't tell whether the calling code expected to read past the end of the disk file. Another developer might maintain that reading past the end of a file is always a possible occurrence as far as most calling code is concerned, and therefore he would design the method to return an EOF status flag rather than throw an exception.
Another point to bear in mind is that when you're designing methods that are going to be called by other developers, you should think carefully about how you're going to handle permission exceptions. The problem is that a detailed permission exception message might give away too much information to a malicious developer. Hackers frequently probe software in order to find out how it behaves, and you sometimes need to be careful about what information you reveal. As an example, the System.IO.File.Exists method in the .NET Framework suppresses any permission exception and instead returns a value of false . I think concealing information like this is bad and that the File.Exists method should instead return a generic security exception that reveals no dangerous information, but you should think carefully about how your application is going to handle these situations.
The best guidance I can give you is to consider carefully what the typical developer calling your method expects, and make the decision that's likely to cause the least confusion and the smallest surprise factor.