What Are the Best Practices for Catching and Re-Throwing Exceptions

What are the best practices for catching and re-throwing exceptions?

You should not be catching the exception unless you intend to do something meaningful.

"Something meaningful" might be one of these:

Handling the exception

The most obvious meaningful action is to handle the exception, e.g. by displaying an error message and aborting the operation:

try {
$connect = new CONNECT($db, $user, $password, $driver, $host);
}
catch (Exception $e) {
echo "Error while connecting to database!";
die;
}

Logging or partial cleanup

Sometimes you do not know how to properly handle an exception inside a specific context; perhaps you lack information about the "big picture", but you do want to log the failure as close to the point where it happened as possible. In this case, you may want to catch, log, and re-throw:

try {
$connect = new CONNECT($db, $user, $password, $driver, $host);
}
catch (Exception $e) {
logException($e); // does something
throw $e;
}

A related scenario is where you are in the right place to perform some cleanup for the failed operation, but not to decide how the failure should be handled at the top level. In earlier PHP versions this would be implemented as

$connect = new CONNECT($db, $user, $password, $driver, $host);
try {
$connect->insertSomeRecord();
}
catch (Exception $e) {
$connect->disconnect(); // we don't want to keep the connection open anymore
throw $e; // but we also don't know how to respond to the failure
}

PHP 5.5 has introduced the finally keyword, so for cleanup scenarios there is now another way to approach this. If the cleanup code needs to run no matter what happened (i.e. both on error and on success) it's now possible to do this while transparently allowing any thrown exceptions to propagate:

$connect = new CONNECT($db, $user, $password, $driver, $host);
try {
$connect->insertSomeRecord();
}
finally {
$connect->disconnect(); // no matter what
}

Error abstraction (with exception chaining)

A third case is where you want to logically group many possible failures under a bigger umbrella. An example for logical grouping:

class ComponentInitException extends Exception {
// public constructors etc as in Exception
}

class Component {
public function __construct() {
try {
$connect = new CONNECT($db, $user, $password, $driver, $host);
}
catch (Exception $e) {
throw new ComponentInitException($e->getMessage(), $e->getCode(), $e);
}
}
}

In this case, you do not want the users of Component to know that it is implemented using a database connection (maybe you want to keep your options open and use file-based storage in the future). So your specification for Component would say that "in the case of an initialization failure, ComponentInitException will be thrown". This allows consumers of Component to catch exceptions of the expected type while also allowing debugging code to access all the (implementation-dependent) details.

Providing richer context (with exception chaining)

Finally, there are cases where you may want to provide more context for the exception. In this case it makes sense to wrap the exception in another one which holds more information about what you were trying to do when the error occurred. For example:

class FileOperation {
public static function copyFiles() {
try {
$copier = new FileCopier(); // the constructor may throw

// this may throw if the files do no not exist
$copier->ensureSourceFilesExist();

// this may throw if the directory cannot be created
$copier->createTargetDirectory();

// this may throw if copying a file fails
$copier->performCopy();
}
catch (Exception $e) {
throw new Exception("Could not perform copy operation.", 0, $e);
}
}
}

This case is similar to the above (and the example probably not the best one could come up with), but it illustrates the point of providing more context: if an exception is thrown, it tells us that the file copy failed. But why did it fail? This information is provided in the wrapped exceptions (of which there could be more than one level if the example were much more complicated).

The value of doing this is illustrated if you think about a scenario where e.g. creating a UserProfile object causes files to be copied because the user profile is stored in files and it supports transaction semantics: you can "undo" changes because they are only performed on a copy of the profile until you commit.

In this case, if you did

try {
$profile = UserProfile::getInstance();
}

and as a result caught a "Target directory could not be created" exception error, you would have a right to be confused. Wrapping this "core" exception in layers of other exceptions that provide context will make the error much easier to deal with ("Creating profile copy failed" -> "File copy operation failed" -> "Target directory could not be created").

Best practices for catching and re-throwing .NET exceptions

The way to preserve the stack trace is through the use of the throw; This is valid as well

try {
// something that bombs here
} catch (Exception ex)
{
throw;
}

throw ex; is basically like throwing an exception from that point, so the stack trace would only go to where you are issuing the throw ex; statement.

Mike is also correct, assuming the exception allows you to pass an exception (which is recommended).

Karl Seguin has a great write up on exception handling in his foundations of programming e-book as well, which is a great read.

Edit: Working link to Foundations of Programming pdf. Just search the text for "exception".

Best practices for Catching and throwing the exception

Generally you can be more explicit.

In this case, you can throw a

ArgumentException

The more specific you are, the easier it is for other code to handle the exception.

This allows you to do

try
{
GetshortMsgCode("arg")
}
catch(ArgumentException e)
{
//something specific to handle bad args, while ignoring other exceptions
}

Is it a good practice to throw exceptions?

I'm sure this question had been answered elsewhere. But here are a few other links for reading:

  • Best practices for catching and re-throwing .NET exceptions
  • When to throw an exception?

From the book "The Pragmatic Programmer", the big question relating to the usage of exceptions is "What is Exceptional?".

In that section, I quote:

... exceptions should rarely be used as part of a program's normal flow; exception should be reserved for unexpected events.

While it is debatable whether or not to use exception in your case, I would say no - because you probably need to capture all possible input errors in one request and reflect back on the form for the user to correct those values.

Now that I recall, yes you should use exceptions here. This is how you code defensively. If you already expected valid arguments to be passed into the Customer class, then the code should throw exception to guard against invalid usage of the class (say for example, by another programmer). In that case, you should have another input validator to validate user's input to the application before reaching the Customer class.

Why catch and rethrow an exception in C#?

First, the way that the code in the article does it is evil. throw ex will reset the call stack in the exception to the point where this throw statement is losing the information about where the exception actually was created.

Second, if you just catch and re-throw like that, I see no added value. The code example above would be just as good (or, given the throw ex bit, even better) without the try-catch.

However, there are cases where you might want to catch and rethrow an exception. Logging could be one of them:

    try 
{
// code that may throw exceptions
}
catch(Exception ex)
{
// add error logging here
throw;
}

How using try catch for exception handling is best practice

My exception-handling strategy is:

  • To catch all unhandled exceptions by hooking to the Application.ThreadException event, then decide:

    • For a UI application: to pop it to the user with an apology message (WinForms)
    • For a Service or a Console application: log it to a file (service or console)

Then I always enclose every piece of code that is run externally in try/catch :

  • All events fired by the WinForms infrastructure (Load, Click, SelectedChanged...)
  • All events fired by third party components

Then I enclose in 'try/catch'

  • All the operations that I know might not work all the time (IO operations, calculations with a potential zero division...). In such a case, I throw a new ApplicationException("custom message", innerException) to keep track of what really happened

Additionally, I try my best to sort exceptions correctly. There are exceptions which:

  • need to be shown to the user immediately

  • require some extra processing to put things together when they happen to avoid cascading problems (ie: put .EndUpdate in the finally section during a TreeView fill)

  • the user does not care, but it is important to know what happened. So I always log them:

  • In the event log

  • or in a .log file on the disk

It is a good practice to design some static methods to handle exceptions in the application top level error handlers.

I also force myself to try to:

  • Remember ALL exceptions are bubbled up to the top level. It is not necessary to put exception handlers everywhere.
  • Reusable or deep called functions does not need to display or log exceptions : they are either bubbled up automatically or rethrown with some custom messages in my exception handlers.

So finally:

Bad:

// DON'T DO THIS; ITS BAD
try
{
...
}
catch
{
// only air...
}

Useless:

// DON'T DO THIS; IT'S USELESS
try
{
...
}
catch(Exception ex)
{
throw ex;
}

Having a try finally without a catch is perfectly valid:

try
{
listView1.BeginUpdate();

// If an exception occurs in the following code, then the finally will be executed
// and the exception will be thrown
...
}
finally
{
// I WANT THIS CODE TO RUN EVENTUALLY REGARDLESS AN EXCEPTION OCCURRED OR NOT
listView1.EndUpdate();
}

What I do at the top level:

// i.e When the user clicks on a button
try
{
...
}
catch(Exception ex)
{
ex.Log(); // Log exception

-- OR --

ex.Log().Display(); // Log exception, then show it to the user with apologies...
}

What I do in some called functions:

// Calculation module
try
{
...
}
catch(Exception ex)
{
// Add useful information to the exception
throw new ApplicationException("Something wrong happened in the calculation module:", ex);
}

// IO module
try
{
...
}
catch(Exception ex)
{
throw new ApplicationException(string.Format("I cannot write the file {0} to {1}", fileName, directoryName), ex);
}

There is a lot to do with exception handling (Custom Exceptions) but those rules that I try to keep in mind are enough for the simple applications I do.

Here is an example of extensions methods to handle caught exceptions a comfortable way. They are implemented in a way they can be chained together, and it is very easy to add your own caught exception processing.

// Usage:

try
{
// boom
}
catch(Exception ex)
{
// Only log exception
ex.Log();

-- OR --

// Only display exception
ex.Display();

-- OR --

// Log, then display exception
ex.Log().Display();

-- OR --

// Add some user-friendly message to an exception
new ApplicationException("Unable to calculate !", ex).Log().Display();
}

// Extension methods

internal static Exception Log(this Exception ex)
{
File.AppendAllText("CaughtExceptions" + DateTime.Now.ToString("yyyy-MM-dd") + ".log", DateTime.Now.ToString("HH:mm:ss") + ": " + ex.Message + "\n" + ex.ToString() + "\n");
return ex;
}

internal static Exception Display(this Exception ex, string msg = null, MessageBoxImage img = MessageBoxImage.Error)
{
MessageBox.Show(msg ?? ex.Message, "", MessageBoxButton.OK, img);
return ex;
}

Understanding exception handling - Rethrowing exceptions

This is described in JDK 7 Rethrowing Exceptions with More Inclusive Type Checking

public void rethrowException(String exceptionName)
throws FirstException, SecondException {
try {
// ...
}
catch (Exception e) {
throw e;
}
}

The Java SE 7 compiler can determine that the exception thrown by the statement throw e must have come from the try block, and the only exceptions thrown by the try block can be FirstException and SecondException. Even though the exception parameter of the catch clause, e, is type Exception, the compiler can determine that it is an instance of either FirstException or SecondException

This analysis is disabled if the catch parameter is assigned to another value in the catch block. However, if the catch parameter is assigned to another value, you must specify the exception type Exception in the throws clause of the method declaration.



Related Topics



Leave a reply



Submit