Where do you like to catch exceptions and why?

2020-01-29 07:15发布

问题:

Where do you like to catch exceptions and why?

I'm interested in seeing where people find it useful to put their try/catch blocks in the hope that some general patterns might emerge. I'll post my two example answers in C++ but any language is fine.

One location and reason per answer please. Thanks.

回答1:

Don't catch anything that you are not prepared to and able to handle.

So, have top-level exception handling in place to bomb the application the way you like on unexpected exceptions and then only catch the stuff you need (as close to where it might be occuring) to get the functionality that is needed.

And you should do only one of two things: actually do something to solve/work around the problem or rethrow a more descriptive exception that has the caught exception as its innerException.

EDIT: If you need a finally block (e.g. to release something you allocated in your code) and you don't have anything useful to do with any exceptions that might pop up the same logic applies: simply don't handle them. Instead, use a catch { throw; } to rethrow the exception to a higher level while keeping all exception info intact. (Or simply omit the catch block, which I think/hope does the same thing?)



回答2:

I try to catch ONLY those exceptions that I can deal with.

I HATE code like this:

      String s="12";
      Integer i;
        try {
          i = Integer.parseInt(s);
        } catch(ParseException pe) {
          System.out.println("hihihihihihihi!!!);
        }

What I especially hate is that what this usually does is to abort the thread anyway, because three lines later there will be an access to i that will assume that i != null.

Then you will read your stacktrace and scroll and scroll and scroll the log til you find the first signifant mistake that made everything else fall apart.

I wish Java didn't force me to catch Exceptions that I cannot deal with, anyway. But what I can do is this:

catch(Exception e) {
  throw new RuntimeException(e);
}

And I declare a lot of "throws" in my function definitions.

I am still dreaming of the day where Eclipse will automatically open a Debugger in the correct line when it will get an uncaught exception. That day, my method will open the correct line.

In other languages, like Smalltalk, I catch only the errors that I can handle. And I happily throw uncaught exceptions when the input does not meet my expectations.

The idea is that I don't want the error to be logged or documented. I want it fixed.



回答3:

I always put a catch in main() as a catch of last resort:

int main( int argc, char** argv ) {
    try {
        Application application( argc, argv );
        return application.result();
    }
    catch ( const std::exception& exception ) {
        fprintf( stderr, "%s.\n", exception.what() );
    }
}


回答4:

In C# and Java I prefer to not catch exceptions at all. If I can't avoid it, I rethrow a RunTime-exception immediately.

I'll always use the greediest catch block that has the largest scope needed, but always with the most specific exception-type possible. Since the outcome of the catch block is another throw in 99.99% of the cases, I try to keep the full method within the try block.



回答5:

I like to try and keep my exception handling code separate from my other code so I usually create a helper method that does the actual logic and the outer method just deals with exception handling. I've always thought this gives the code a clean look and makes it more readable.

public void stuff() throws MyException
{
    try
    {
        tryStuff();
    }
    catch (SomeLibraryException e)
    {
        logger.log("Some message happened", e);
        throw new MyException(e);
    }
}

public void tryStuff() throws SomeLibraryException
{
   // Do things in here that could throw exceptions
}


回答6:

I like to catch around handlers in the controller that handle events fired from the view. If exceptions give the strong guarantee of safety this is a useful catch point because it is high level enough to report the error and the handlers are usually atomic and related to something that the user has just done so hopefully they'll be able to work out what's going on.

void Controller::on_edit_entity( const Entity& entity ) {
    try {
        Command::ptr command = new EditEntityCommand( entity );
        push_command( command );
    }
    catch ( const std::exception& exception ) {
        fprintf( stderr, "%s.\n", exception.what() );
    }
}


回答7:

In a Delphi Windows Application, the main message handling loop for your application (i.e. at the bottom of the call stack) handles your exceptions by showing a message box. In my opinion this is the best place to handle exceptions.

By catching exceptions in your own methods for the sole purpose of showing a message box to the user, you're denying any code calling your method of knowing that an exception actually occured and that the method infact failed.

I would only handle an exception if:

  • I need to do some cleanup (like a DB rollback) in which case I would reraise the exception after the cleanup is done.
  • I have some more information to add to the exception.
  • My method can succeed in it's purpose despite the exception.


回答8:

(Before I begin: I am a Java guy)
What I recommend:

  1. Find the closest point up the call chain from the exception source where you can handle the exception properly - i.e take corrective measures, signal failure of the transaction/action etc. (Logging by itself should not be considered as handling the exceptions) All the methods between the handler and the thrower should ignore the exception. Prefer unchecked exceptions to checked ones so that the exceptions don't even figure in all those intermediate methods.
  2. Layer boundaries and APIs should specify the exceptions it can throw using checked exceptions since handling those exceptions is part of the contract for the client layer/code that uses it.
  3. Write an exception handler class with a handle(Exception e) method and release that to the team initially and ensure that everyone uses it to handle exceptions. Based on changing exception handling scenarios, keep adding overloaded 'handle' methods later on so that only the handler need to be modified.
  4. Always remember to chain exceptions when doing catch-and-throw. This ensures that the full cause of exception gets reported.
  5. Never log the same exception trace more than once. It makes it very hard to debug using log files.
  6. Top level methods should have a catch clause that will catch any exception that the system may throw. This prevents spilling of our internal information to the outside world if, god forbid, things go wrong in production environment. This is more of a security requirement.


回答9:

Two places:

  • In all event handlers
  • Any location where the catch can do something useful, like supplement the exception with information that will be useful for debugging. Typically a new exception will be created with this information and thrown. Note that the InnerException (or non-.NET equivalent) of this new exception should be set to that of the original exception, in order that things like the stack trace are preserved.