37

I've seen the following code many times:

try
{
    ... // some code
}
catch (Exception ex)
{
    ... // Do something
    throw new CustomException(ex);

    // or
    // throw;

    // or
    // throw ex;
}

Can you please explain the purpose of re-throwing an exception? Is it following a pattern/best practice in exception handling? (I've read somewhere that it's called "Caller Inform" pattern?)

4

13 回答 13

44

Rethrowing the same exception is useful if you want to, say, log the exception, but not handle it.

Throwing a new exception that wraps the caught exception is good for abstraction. e.g., your library uses a third-party library that throws an exception that the clients of your library shouldn't know about. In that case, you wrap it into an exception type more native to your library, and throw that instead.

于 2008-09-22T07:15:12.193 回答
26

Actually there is a difference between

throw new CustomException(ex);

and

throw;

The second will preserve the stack information.

But sometimes you want to make the Exception more "friendly" to your application domain, instead of letting the DatabaseException reach your GUI, you'll raise your custom exception which contains the original exception.

For instance:

try
{

}
catch (SqlException ex)
{
    switch  (ex.Number) {
        case 17:
        case 4060:
        case 18456:
           throw new InvalidDatabaseConnectionException("The database does not exists or cannot be reached using the supplied connection settings.", ex);
        case 547:
            throw new CouldNotDeleteException("There is a another object still using this object, therefore it cannot be deleted.", ex);
        default:
            throw new UnexpectedDatabaseErrorException("There was an unexpected error from the database.", ex);
    } 
}
于 2008-09-22T07:26:42.227 回答
12

Sometimes you want to hide the implementation details of a method or improve the level of abstraction of a problem so that it’s more meaningful to the caller of a method. To do this, you can intercept the original exception and substitute a custom exception that’s better suited for explaining the problem.

Take for example a method that loads the requested user’s details from a text file. The method assumes that a text file exists named with the user’s ID and a suffix of “.data”. When that file doesn’t actually exist, it doesn’t make much sense to throw a FileNotFoundException because the fact that each user’s details are stored in a text file is an implementation detail internal to the method. So this method could instead wrap the original exception in a custom exception with an explanatory message.

Unlike the code you're shown, best practice is that the original exception should be kept by loading it as the InnerException property of your new exception. This means that a developer can still analyze the underlying problem if necessary.

When you're creating a custom exception, here's a useful checklist:

• Find a good name that conveys why the exception was thrown and make sure that the name ends with the word “Exception”.

• Ensure that you implement the three standard exception constructors.

• Ensure that you mark your exception with the Serializable attribute.

• Ensure that you implement the deserialization constructor.

• Add any custom exception properties that might help developers to understand and handle your exception better.

• If you add any custom properties, make sure that you implement and override GetObjectData to serialize your custom properties.

• If you add any custom properties, override the Message property so that you can add your properties to the standard exception message.

• Remember to attach the original exception using the InnerException property of your custom exception.

于 2008-09-22T07:44:16.617 回答
10

You typically catch and re-throw for one of two reasons, depending on where the code sits architecturally within an application.

At the core of an application you typically catch and re-throw to translate an exception into something more meaningful. For example if you're writing a data access layer and using custom error codes with SQL Server, you might translate SqlException into things like ObjectNotFoundException. This is useful because (a) it makes it easier for callers to handle specific types of exception, and (b) because it prevents implementation details of that layer such as the fact you're using SQL Server for persistence leaking into other layers, which allows you to change things in the future more easily.

At boundaries of applications it's common to catch and re-throw without translating an exception so that you can log details of it, aiding in debugging and diagnosing live issues. Ideally you want to publish error somewhere that the operations team can easily monitor (e.g. the event log) as well as somewhere that gives context around where the exception happened in the control flow for developers (typically tracing).

于 2008-09-22T07:25:52.807 回答
2

I can think of the following reasons:

  • Keeping the set of thrown exception types fixed, as part of the API, so that the callers only have to worry about the fixed set of exceptions. In Java, you are practically forced to do that, because of the checked exceptions mechanism.

  • Adding some context information to the exception. For example, instead of letting the bare "record not found" pass through from the DB, you might want to catch it and add "... while processing order no XXX, looking for product YYY".

  • Doing some cleanup - closing files, rolling back transactions, freeing some handles.

于 2008-09-22T07:30:19.537 回答
1

Generally the "Do Something" either involves better explaining the exception (For instance, wrapping it in another exception), or tracing information through a certain source.

Another possibility is if the exception type is not enough information to know if an exception needs to be caught, in which case catching it an examining it will provide more information.

This is not to say that method is used for purely good reasons, many times it is used when a developer thinks tracing information may be needed at some future point, in which case you get try {} catch {throw;} style, which is not helpful at all.

于 2008-09-22T07:15:13.957 回答
1

I think it depends on what you are trying to do with the exception.

One good reason would be to log the error first in the catch, and then throw it up to the UI to generate a friendly error message with the option to see a more "advanced/detailed" view of the error, which contains the original error.

Another approach is a "retry" approach, e.g., an error count is kept, and after a certain amount of retries that's the only time the error is sent up the stack (this is sometimes done for database access for database calls that timeout, or in accessing web services over slow networks).

There will be a bunch of other reasons to do it though.

于 2008-09-22T07:15:14.517 回答
1

FYI, this is a related question about each type of re-throw: Performance Considerations for throwing Exceptions

My question focuses on "Why" we re-throw exceptions and its usage in application exception handling strategy.

于 2008-09-22T07:17:52.140 回答
0

Until I started using the EntLib ExceptionBlock, I was using them to log errors before throwing them. Kind of nasty when you think I could have handled them at that point, but at the time it was better to have them fail nastily in UAT (after logging them) rather than cover a flow-on bug.

于 2008-09-22T07:13:00.903 回答
0

The application will most probably be catching those re-thrown exceptions higher up the call stack and so re-throwing them allows that higher up handler to intercept and process them as appropriate. It is quite common for application to have a top-level exception handler that logs or reports the expections.

Another alternative is that the coder was lazy and instead of catching just the set of exceptions they want to handle they have caught everything and then re-thrown only the ones they cannot actually handle.

于 2008-09-22T07:14:18.077 回答
0

As Rafal mentioned, sometimes this is done to convert a checked exception to an unchecked exception, or to a checked exception that's more suitable for an API. There is an example here:

http://radio-weblogs.com/0122027/stories/2003/04/01/JavasCheckedExceptionsWereAMistake.html

于 2010-07-30T16:52:44.743 回答
0

If you look at exceptions as on an alternative way to get a method result, then re-throwing an exception is like wrapping your result into some other object.

And this is a common operation in a non-exceptional world. Usually this happens on a border of two application layers - when a function from layer B calls a function from layer C, it transforms C's result into B's internal form.

A -- calls --> B -- calls --> C

If it doesn't, then at the layer A which calls the layer B there will be a full set of JDK exceptions to handle. :-)

As also the accepted answer points out, layer A might not even be aware of C's exception.

Example

Layer A, servlet: retrieves an image and it's meta information
Layer B, JPEG library: collects available DCIM tags to parse a JPEG file
Layer C, a simple DB: a class reading string records from a random access file. Some bytes are broken, so it throws an exception saying "can't read UTF-8 string for record 'bibliographicCitation'".

So A won't understand the meaning of 'bibliographicCitation'. So B should translate this exception for A into TagsReadingException which wraps the original.

于 2013-11-06T23:24:25.077 回答
-3

THE MAIN REASON of re-throwing exceptions is to leave Call Stack untouched, so you can get more complete picture of what happens and calls sequence.

于 2008-09-22T07:17:50.847 回答