views:

721

answers:

9
catch(Exception ex)
{

}

What's the best way to proceed?

Rip them all out and let it crash? Add logging code? Message boxes? This is in C#.

+23  A: 

It partly depends on how aggressive you can be. Is this app internal or external? Will your changes be deployed on live systems soon? Do you have specific bugs to fix, or is it just deemed to be a disaster?

To reduce the bug count as quickly as possible, but with the most risk of serious damage, just remove all the catch blocks and let exceptions bubble up. For a much more delicate approach, just add logging to start with.

You should also talk to whoever wrote the app to start with, if possible. Try to find out why they've got so many exception-swallowing blocks. Do they really understand exceptions at all? A certain amount of tact is necessary here, I suspect :)

Next stop: unit tests...

Jon Skeet
Seems pretty evident that they *don't* understand exceptions. They wanted a .NET way to write `ON ERROR RESUME NEXT`, and they found it.
Aaronaught
@Aaronaught: It certainly sounds that way - but I'd at least want to hear their side of the story.
Jon Skeet
I'de be pretty nervous if Jon Skeet walked up to my desk!
vidalsasoon
*"Why did I write that? Well, you see, we kept getting these messages about unhandled exceptions, so I asked online and somebody told me I needed to 'handle' the exceptions using `catch`. Why, is there a problem?"*
Aaronaught
+3  A: 

wow....

I would be hesitant to rip them out because odds are that would just create more "bugs". A first step would be to add logging so you know what is going on. After you have sufficient information you would be able to proceed refactoring....carefully

Unit tests would be a great way to test any refactoring you do. I would also suggest getting plenty of them in place.

Joe
Chances are it wouldn't be *creating* more bugs - just *exposing* more bugs.
Jon Skeet
yeah, that is what I have bugs in quotes :). To the end user you could have created a bug, but really you just exposed an existing one.
Joe
Unit tests can be a real pain if the app is WinForms heavy ... Gawd why don't computers make our lives easier?
Hamish Grubijan
IMO Unit Tests are a waste of time on very badly designed applications. I would refactor then add the unit tests.
vidalsasoon
@Hamish: unit tests work fine in winforms applications. You just make sure to separate the business logic and data access from the presentation (forms). It's easy to test anything except the user interface itself.
John Saunders
@vidal: even in a badly-designed application, unit tests can be valuable. This assumes that it's possible to determine what the application is meant to do, but unit tests can then be used to confirm that your refactoring hasn't broken anything. Management likes that kind of reassurance.
John Saunders
@John, in most of the real competitive world code is not written that way from the start.
Hamish Grubijan
@Hamish: I didn't say it was written that way from the start. I meant that's the way professional developers write WinForms code.
John Saunders
A: 

One approach you could take in order to get a picture of what's going on would be to take each of the empty Exception blocks, and have each one call a method breakOnException(Exception e).

This method doesn't have to do anything except (say) log it. You can then run the program under a debugger with a breakpoint on this method and watch for the breakpoint to be hit, and then investigate the causes. Unfortunately that's a little labour-intensive.

Do you have unit tests that can be run against the code in the debugger ? It would make sense to do the above with these.

Brian Agnew
Instead of calling a method named 'breakOnException', I would configure Visual Studio to break on all exceptions, by going to Debug / Exceptions... / Common Language Runtime Exceptions and check the 'Thrown' checkbox. This has the same result, without having to alter the code.
Steven
Good point. I'm not familiar with VS debugging, so the above makes sense. My main point about running in the debugger remains.
Brian Agnew
+2  A: 

Caveat: This is general advice and quirks in your environment may make it impossible, e.g. time pressures.

Rip them all out with the exception of at the entry points to the system (main methods, service end points, top of thread call stacks, event handlers - for UI code) and add logging/error handling to the places where the exception handlers remain.

The begin a process of adding the handlers back in where required, i.e. places where exceptions should be handled lower down, with appropriate logging/error reporting.

Getting the system working again is dependent on having a good set of acceptance/regression tests to verify the system is correct after all the changes are made.

Michael Barker
This advice is much like the advice I gave, so +1 for this ;-).
Steven
+13  A: 

Fix the bugs that you were set out to do, and report the exception swallowing blocks as new, critical, bugs.

Guffa
I like your answer. This allows your manager to have a better overview on the number of bugs and allows him to scedule a repare.
Steven
+12  A: 

The first intermediate goal is to get a good picture of what exceptions are being ignored and where; for that purpose, you can simply add logging code to each of those horrid catch-everything blocks, showing exactly what block it is, and what is it catching and hiding. Run the test suite over the code thus instrumented, and you'll have a starting "blueprint" for the fixing job.

If you don't have a test suite, then, first, make one -- unit tests can wait (check out Feathers' great book on working with legacy code -- legacy code is most definitely your problem here;-), but you need a suite of integration tests that can be run automatically and tickle all the bugs you're supposed to fix.

As you go and fix bug after bug (many won't be caused by the excessively broad catch blocks, just hidden/"postponed" by them;-), be sure to work in a mostly "test-driven" manner: add a unit test that tickes the bug, confirm it breaks, fix the bug, rerun the unit test to confirm the bug's gone. Your growing unit test suite (with everything possible mocked out or faked) will run fast and you can keep rerunning cheaply as you work, to catch possible regressions ASAPs, when they're still easy to fix.

The kind of task you've been assigned is actually harder (and often more important) than "high prestige" SW development tasks such as prototypes and new architectures, but often misunderstood and under-appreciated (and therefore under-rewarded!) by management/clients; make sure to keep a very clear and open communication channel with the stakeholder, pointing out all the enormous amount of successful work you're doing, how challenging it is, and (for their sake more than your own), how much they would have saved by doing it right in the first place (maybe next time they will... I know, I'm a wild-eyed optimist by nature;-). Maybe they'll even assign you a partner on the task, and you can then do code reviews and pair programming, boosting productivity enormously.

And, last but not least, good luck!!! -- unfortunately, you'll need it... fortunately, as Jefferson said, "the harder I work, the more luck I seem to have";-)

Alex Martelli
+1 for referencing Working With Legacy Code.
Steven
+3  A: 

The solution you should pick depends greatly on the environment you're working in.

The coding guidelines I've written and introduced to most of my customers, state explicitly that empty catch clauses without comments (exactly as you've shown in your question) may be removed without any discussion. The reason I wrote this rule is because I encounter these kind of statements all the time and they almost always hide a lot of bugs. Usually, the more code is in the try block, the more bugs they hide. Over the years I have discovered (and solved) a lot of bugs by simply removing empty catch clauses. The procedure is usually the same:

  1. I remove a catch.
  2. Code goes to production.
  3. After some time customer complaints that the program stopped working (he got an exception).
  4. I start investigating the problem and find out that this part of system never really worked, and multiple developers have tried to find bugs related to this cause, but never found the problem. Or worse, they found the problem and wrote that catch statement to ‘solve’ the problem.
  5. I fix the real problem that was swept under the carpet and close multiple bug reports at once.

While this method has served me (and my customers) well these years, there is a ‘but’. For instance, one of my current customers is a medical institution and the developers were interested in using my coding guidelines. However, I insisted that they'd remove that particular rule from the guidelines. While their software has a terribly lot of those empty catch statements (well over 100) and those pesky things hide a lot of errors and byte me all the time while I work with their software; you must be very careful in these types of applications. In this scenario, I would start by adding log statements, instead of removing them. After this you can remove them slowly one-by-one when you know which types of exceptions occur and why the previous developer did add that catch statement in the first place.

In all cases, you should have a general catch statement at the top of your application (or have an exception handler in your web app) that will log any exceptions that bubble up.

Extra note, my guidelines also note that you would configure Visual Studio to break on all exceptions, by going to Debug / Exceptions... / Common Language Runtime Exceptions and check the 'Thrown' checkbox. This way you spot an exception right away.

Steven
+1  A: 

Obviously logging all suppressed exceptions is the first place to start - blanket suppression is useful from time to time when you need to ensure that your code gracefully degrades under all circumstances (i.e. where you don't wish to be caught out by an exception type that you failed to anticipate), but can hide an unanticipated problem that needs more handling than simply being ignored, so it is imperative that all exception handlers should at a minimum report that an exception has been suppressed, so you have a clear trail of clues to follow if an unexpected behaviour emerges.

Removing the catches is foolish, as there will be many cases where some exceptions need to be "handled" for the program to work correctly (although you may disagree with how they are handled, and there may be risks associated with this approach, the original author had a reason for writing the code this way. Ig he has not added a comment explaining his reason, then don't assume you know better than he did, especially not in a "global search and replace" manner).

However, nobody seems to have pointed out the most obvious, and quickest to implement, starting point: Go to "Debug -> Exceptions" and enable "Break when exception is thrown" for the "Common Language Runtime Exceptions" section. This will cause the debugger to break for every exception that is thrown (before the program's catch block is called to handle it), so you will be able to test the application under the debugger and determine which catch blocks are suppressing which exceptions when you try to repeat a given bug, from which you can then make a judgement call as to whether or not that particular catch block has any influence on the bug in question.

Jason Williams
An empty catch block catching `Exception` does not constitute "handling" an exception.
John Saunders
@John: Rubbish. In many circumstances an exception is *expected* in normal operation, and is *not an error*. In a few of those cases you don't need to actually do anything to "recover from" the event (e.g. You can get a SocketException while your thread is waiting to read from a socket if it is closed by another thread. This is normal, there is nothing you can do about it, and your program needs take no action *other than to catch the exception* to stop the program "crashing" due to an unhandled exception.
Jason Williams
Another pattern: You wish to read some info from a file. In three cases where it fails (DirectoryNotFound, FileNotFound, AccessDenied) you want to return default values. So you set up default values first, then try the read, and if an exception occurs, you need to catch it *but do nothing* and just return. This saves you filling in those defaults 3 times.
Jason Williams
Sure, catching base class Exception is bad, and (as I said) not logging the exception is bad, but empty catch blocks are run of the mill, and perfectly ok as long as there is a good reason and that reason is explained in the code.
Jason Williams
@Jason: glad you don't work for me or with me. We're talking about an empty catch block catching the `Exception` type. That is never a valid way to "handle" exceptions, unless you're in some situation where you cannot have any idea what type of exceptions to expect. In your example of a `SocketException`, the program should be catching `SocketException` and not `Exception`, and the catch block should be commented to say that `SocketException` is expected. In any case, would you not test to make sure that you've received an _expected_ `SocketException` and not an unexpected one?
John Saunders
@Jason: again, in your file read case, you should be catching a more-specific exception. In fact, don' be lazy: catch all three and do nothing (except add a comment saying why the exception is expected).
John Saunders
@John: Sorry, didn't realise you were talking *only* about catching Exception or catch(). I never said you should do this. e.g. in the File example, I was talking about catching three *specific* exception types, but having an "empty implementation" in the catch block simply because no "extra handling" (beyond catching the exception to stop it propagating) is required by the code design.
Jason Williams
My original point was simply that you should not jump in all guns blazing and delete a block of code (e.g. catch{}) if you don't have any idea why it was written that way! Instead, add logging and debug through the code with break-on-exception enabled until you *understand* it, before making any changes. That would be stupid because blindly deleting bits of someone else code are likely to introduce more problems than they solve. Take a measured and sane approach to such "refactoring".
Jason Williams
+7  A: 

Change the catch blocks as follows:

catch (Exception ex)
{
    Logger.Log(String.Format(
        System.Globalization.CultureInfo.InvariantCulture,
        "An exception is being eaten and not handled. "+
        "This may be hiding critical errors.\n"+
        "Exception: {0}",
        ex));
}
John Saunders
+1 for that. Every Empty Catch block has to be evaluated on it's own, as sometimes it may be appropriate to ignore exceptions and sometimes you could end up opening Pandoras Can of Worms by trying to remove one.
Michael Stum