Friday, April 9, 2010

Exceptions


Exception throwing guidelines described in this section require a good definition of the meaning of execution failure. Execution failure occurs whenever a member cannot do what it was designed to do (what the member name implies). For example, if OpenFile method cannot return an opened file handle to the caller, it would be considered an execution failure.
Most developers have become comfortable with using exceptions for hard error cases such as division by zero or null references. In the Framework, exceptions are used for both hard errors and logical errors. At first, it can be difficult to embrace exception handling as the means of reporting all functional failures. However, it is important to design all public methods of a framework to report method-failures by throwing an exception.
There are a variety of excuses for not using exceptions, but most boil down to the two perceptions that exception handling syntax is undesirable, so returning an error code is somehow preferable, or that a thrown exception does not perform as well as returning an error code. The performance concerns are addressed in the performance section below. The concern over syntax is largely a matter of familiarity and should not be a consideration. As an API designer we should not make assumptions about the familiarity of the application developers consuming our code. 
Do not return error codes. Exceptions are the primary means of reporting errors in frameworks.
Do report execution failures by throwing exceptions. If a member cannot successfully do what is designed to do, it should be considered an execution failure and an exception should be thrown.
Consider terminating the process by calling System.Environment.FailFast (.NET Framework 2.0 feature) instead of throwing an exception, if your code encounters a situation where it is unsafe for further execution.
Do not use exceptions for normal flow of control. Except for system failures, there should generally be a way to write code that avoids exceptions being thrown. For example, you can provide a way to check preconditions before calling a member to allow users to write code that does not throw exceptions.
ICollection collection = …
if(!collection.IsReadOnly){
    collection.Add(additionalNumber);
}
The member used to check preconditions of another member is often referred to as a tester and the member that actually does the work is called a doer. See performance section below for more information on the Tester-Doer Pattern.
There are cases when the Tester-Doer pattern may have an unacceptable performance overhead. In such cases the so called TryParse Pattern (see section below) should be used.
Consider performance implications of throwing exceptions. See section below for details.
Do document all exceptions thrown by publicly callable members because of a violation of the member contract (rather than a system failure) and treat them as part of your contract. Exceptions that are a part of the contract should not change from one version to the next.
Do not have public members that can either throw or not based on some option.
Type GetType(string name, bool throwOnError)
Do not have public members that return exceptions as the return value or an out parameter.
Do set all the relevant properties of the exception you throw.
Consider using exception builder methods. It is common to throw the same exception from different places. To avoid code bloat, use helper methods that create exceptions and initialize their properties. For example:
class File{
   string fileName;

   public byte[] Read(int bytes){
      if (!ReadFile(handle, bytes))
            throw NewFileIOException(...);
   }

   FileException NewFileException(...){
      string description = // build localized string
      return new FileException(description);
   }
}
Do not throw exceptions from exception filter blocks. When an exception filter raises an exception, the exception is caught by the CLR, and the filter returns false. This behavior is indistinguishable from the filter executing and returning false explicitly and is therefore very difficult to debug.
Avoid explicitly throwing exceptions from finally blocks. Implicitly thrown exceptions resulting from calling methods that throw are acceptable. 



Designing Custom Exceptions
In some cases, it will not be possible to use existing exceptions. In those cases, you’ll need to define custom exceptions. The guidelines in this section provide help on doing that.
Avoid deep exception hierarchies. 
Do derive exceptions from System.Exception or one of the other common base Exceptions.
Do end exception class names with the ‘Exception’ suffix.
Do make exceptions serializable. An exception must be serializable to work correctly across application domain and remoting boundaries.
Do provide (at least) these common constructors on all exceptions. Make sure the names and types of the parameters are exactly as in the example below.
public class SomeException: Exception, ISerializable {
   public SomeException();
  public SomeException(string message);
  public SomeException(string message, Exception inner);

  // this constructor is needed for serialization.
   protected SomeException(SerializationInfo info, StreamingContext context);
}
Do report security sensitive information through an override of ToString only after demanding an appropriate permission.
If the permission demand fails, return a string excluding the security sensitive information.
Annotation (Rico Mariani):
Do not store the results of ToString in any generally accessible data structure unless that data structure suitably secures the string from untrusted code.  This advice applies to all strings but since exception strings frequently contain sensitive information (such a file paths) I reiterate the advice here.
Do store useful security sensitive information in private exception state. Ensure only trusted code can get the information.
Consider providing exception properties for programmatic access to extra information (besides the message string) relevant to the exception.


Application Exception :