Exception Handling
The following guidelines help ensure that your library handles exceptions appropriately.
Do not handle errors by catching non-specific exceptions, such as System.Exception, System.SystemException, and so on, in framework code.
You can catch exceptions when the purpose of catching the exception is to re-throw or transfer the exception to a different thread. The following code example demonstrates incorrect exception handling.
public class BadExceptionHandlingExample1
{
public void DoWork()
{
// Do some work that might throw exceptions.
}
public void MethodWithBadHandler()
{
try
{
DoWork();
}
catch (Exception e)
{
// Swallow the exception and continue
// executing.
}
}
}
Avoid handling errors by catching non-specific exceptions, such as System.Exception, System.SystemException, and so on, in application code. There are cases when handling errors in applications is acceptable, but such cases are rare.
An application should not handle exceptions that can result in an unexpected or exploitable state. If you cannot predict all possible causes of an exception and ensure that malicious code cannot exploit the resulting application state, you should allow the application to terminate instead of handling the exception.
Do not exclude any special exceptions when catching for the purpose of transferring exceptions.
Instead of creating lists of special exceptions in your catch clauses, you should catch only those exceptions that you can legitimately handle. Exceptions that you cannot handle should not be treated as special cases special-cased in non-specific exception handlers. The following code example demonstrates incorrectly testing for special exceptions for the purposes of re-throwing them.
public class BadExceptionHandlingExample2
{
public void DoWork()
{
// Do some work that might throw exceptions.
}
public void MethodWithBadHandler()
{
try
{
DoWork();
}
catch (Exception e)
{
if (e is StackOverflowException ||
e is OutOfMemoryException)
throw;
// Handle exception and continue
// executing.
}
}
}
Consider catching specific exceptions when you understand why it will be thrown in a given context.
You should catch only those exceptions that you can recover from. For example, a FileNotFoundException that results from an attempt to open a non-existent file can be handled by an application because it can communicate the problem to the user and allow the user to specify a different file name or create the file. A request to open a file that generates an ExecutionEngineException should not be handled because the underlying cause of the exception cannot be known with any degree of certainty, and the application cannot ensure that it is safe to continue executing.
Do not overuse catch. Exceptions should often be allowed to propagate up the call stack.
Catching exceptions that you cannot legitimately handle hides critical debugging information.
Do use try-finally and avoid using try-catch for cleanup code. In well-written exception code, try-finally is far more common than try-catch.
The purpose of a catch clause is to allow you to handle exceptions (for example, by logging a non-fatal error). The purpose of a finally clause is to allow you to execute cleanup code regardless of whether an exception was thrown. If you allocate expensive or limited resources such as database connections or streams, you should put the code to release them inside a finally block.
Do prefer using an empty throw when catching and re-throwing an exception. This is the best way to preserve the exception call stack.
The following code example shows a method that can throw an exception. This method is referenced in later examples.
public void DoWork(Object anObject)
{
// Do some work that might throw exceptions.
if (anObject == null)
{
throw new ArgumentNullException("anObject",
"Specify a non-null argument.");
}
// Do work with o.
}
The following code example demonstrates catching an exception and incorrectly specifying it when re-throwing the exception. This causes the stack trace to point to the re-throw as the error location, instead of pointing to the DoWork
method.
public void MethodWithBadCatch(Object anObject)
{
try
{
DoWork(anObject);
}
catch (ArgumentNullException e)
{
System.Diagnostics.Debug.Write(e.Message);
// This is wrong.
throw e;
// Should be this:
// throw;
}
}
Do not handle non-CLS-compliant exceptions (exceptions that do not derive from System.Exception) using a parameterless catch block. Languages that support exceptions that are not derived from Exception are free to handle these non-CLS compliant exceptions.
The .NET Framework version 2.0 wraps non-CLS-compliant exceptions in a class derived from Exception.
Portions Copyright 2005 Microsoft Corporation. All rights reserved.
Portions Copyright Addison-Wesley Corporation. All rights reserved.
For more information on design guidelines, see the "Framework Design Guidelines: Conventions, Idioms, and Patterns for Reusable .NET Libraries" book by Krzysztof Cwalina and Brad Abrams, published by Addison-Wesley, 2005.
See Also
Other Resources
Design Guidelines for Developing Class Libraries
Design Guidelines for Exceptions