A first hand look from the .NET engineering teams
You’ve seen the advice before—it’s not a good programming practice to catch System.Exception. Because managed exceptions are hierarchical, catching the top of the hierarchy—e.g., catch(Exception)—is an easy way to make sure that you catch all exceptions. But do you really want to catch all exceptions?
Empty catch statements can be just as bad, depending on the MSIL code that your language generates. C# turns an empty catch statement into catch(System.Object) which means you end up catching all exceptions—even non-CLS compliant exceptions. VB is better-behaved, turning an empty catch statement into catch e as System.Exception which limits you to catching CLS compliant exceptions.
Instead, you should catch the exact exception types that you expect because these are the types your code is prepared to handle. Obviously, any code that throws an exception is a good candidate for corresponding catch. MSDN documents all exceptions that can be raised from BCL functions. For example, System.IO.StreamReader () can throw ArgumentException or ArgumentNullException. If you use a StreamReader you should catch—and handle—these two exceptions.
We sometimes see advice that suggests you should catch all exceptions. In Eric Brechner’s Hard Code column he said that letting an application crash and invoke Windows Error Reporting (commonly called “Watson” from the pre-Vista days) was “completely idiotic and irresponsible.” He suggests that instead you catch all exceptions and “try to correct” them.
There’s one small problem with Eric’s advice: the word “try”. When you catch an exception you MUST handle the exceptional condition. Catching an exception is a statement that you can handle the exception and restore the program to a non-exceptional state. Trying to handle an exception isn’t sufficient. Follow the advice of someone older and wiser: “Do, or do not. There is no ‘try’.”
One key problem is that our exception hierarchy is a single mechanism representing multiple exceptional conditions. Krzysztof Cwalina explains this pretty well, breaking exceptional conditions into three sets: usage errors, logical errors and system failures. Usage errors are a condition that should always be fixed by the developer—for example, if an API cannot take null, the developer should ensure that she doesn’t pass in null. Logical errors can’t be detected at development time but should be handled at program runtime by the program itself. For example, a FileNotFoundException should be handled by creating the file or prompting the user for a new filename. System failures, however, cannot be handled by code. System failures indicate that something has gone wrong outside of your program’s scope. Your program can not be expected to correct a system failure. Thus the suggestion that you “try to correct” the error condition cannot reasonably be applied to system failures.
Let’s say your program causes an access violation and you catch the AccessViolationException. What can you do now? Find the pointer which has the bad value and set it to point to the right memory? If you could do that, you wouldn’t have AV’d in the first place. Catching the exception is the wrong thing to do if you can’t correct the error. In the case of a system failure you can’t correct the error.
We’re making a change in Visual Studio 2010’s CLR to make it harder to make this mistake. When an exception could indicate a corrupted process state—what Krzysztof calls a system failure—you’ll have to use a new attribute to indicate your intention explicitly. You can read about the details in the CLR Inside Out column in February’s MSDN Magazine. Even with our Corrupted State Exceptions work, catching all exceptions is not a good idea. If you can’t correct the condition that resulted in an exception being raised you’re always much better off Watsoning up (that is, letting your program exit and invoke Windows Error Reporting.) Watson will report back from the user’s machine so that you can fix the error in your next servicing or release.
Program Manager, CLR
One reason I catch exceptions (and I agree with one of the earlier comments about putting the application back in a consistent state) is to add more information to the exception. Sometimes, the conditions into the situation led to the exception in an unforeseen way. Adding addition context (parameter state, etc..) and letting the exception bubble back up, i find very helpful.
Hi all, the above principal just generally relies on what you are doing. I agree with the poster who states that there are generally no rules to programming, there is usually more than one way to skin a cat so to speak.
Personally I become very irritated by applications which just crash (Visual Studio being one of them) rather than just throwing up an exception either to a log file or the user and handling the error. In virtually all cases you do want to handle the exception back (top level handler), do you really want to put in an exception block for every potential exception which could be thrown from a 3rd party component? No, you wouldn't and it would be irresponsible to not have some sort of exception handling to check.
All in all, there is nothing worse than seeing that bloody Watson tool or some from of office reporting tool in the background taking up my CPU usage.