- Do end Exception class names end in “Exception”.
public class FileNotFoundException : Exception {
}
- Recommendation using at least these common constructors.
public class XxxException : Exception {
XxxException() { }
XxxException(string message) { }
XxxException(string message, Exception inner) { }
}
- Recommendation using the predefined exceptions types. Only define new exception types for programmatic scenarios.
Introduce a new exception class so a programmer can take a different action in code based the exception class. Do not define a new exception class unless there is a scenario for a developer needing the exception class.
For example, it makes sense to define FileNotFoundException because the programmer might decide to create the missing file, whereas FileIOException is not something that would typically be handled specifically in code.
- Do make sure that in exceptions which don't have an explicit message (read: non-null), the message property traverses down the tree to the next real text.
SethDe: add an example
- Do not derive new exceptions from the base class Exception. The base class will have multiple subclasses according to individual namespace.
- Do remember that under the system exception will be another container exception called Core, which will contain the handful of EE exceptions that bring the system completely down. Users cannot throw these exceptions.
- Do group new exceptions off the base class Exception by namespace. For example, there will be subclasses for XML, IO, Collections, etc.
Each of these areas will subclass their own exceptions as appropriate. Any exceptions that other library or application writers wish to add will extend exception directly. A single name for all related exceptions should be made, and all exceptions related to that application or library should extend from that bucket
- Do use a localized description string. When the user sees an error message, it will be derived from the description string of the exception that was thrown, and never from the exception class. Include a description string in every exception.
- Do use grammatically correct error messages including ending punctuation.
Each sentence in a description string of an exception ends in a period. Code that generically displays an exception message to the user handles the case where a developer forgot the final period.
- Do provide Exception properties for programmatic access. Include extra information in an exception (besides the description string) only when there is a programmatic scenario where that additional information is useful. It's rare to need to include additional information in an exception.
- Do throw exceptions only in exceptional cases.
- Do not use exceptions for normal or expected errors.
- Do not use exceptions for normal flow of control.
- Recommendation returning null for extremely common error cases. For example, File.Open returns a null if the file is not found, but throws and exception if the file is locked.
- Do design classes such that in the normal coarse of use there will never be an exception thrown. For example, a FileStream class exposes another way of determining if the end of file has been reached to avoid the exception that will be thrown if the developer reads past the end of the file. For example:
class Foo {
void Bar() {
FileStream stream = File.Open("myfile.txt");
byte b;
// ReadByte returns -1 at EOF
while ((b =
stream.ReadByte()) > = 0) {
// do something
}
}
}
- Do throw an InvalidOperationException if in an inappropriate state.
The System.InvalidOperationException exception is supposed to be thrown if the property set or method call is not appropriate given the objects current state.
- Do throw an ArgumentException or subclass there of if passed bad parameters.
When bad parameters are detected, throw a System.ArgumentException or a subclass there of.
- Do realize that the stack trace starts at the throw.
The stack trace is from where the exception is thrown, not where it is newed. Be aware of this fact when deciding where to throw.
- Recommendation using exception builder methods.
It's common for a class to throw the same exception from different places in its implementation. To avoid code bloat, use helper methods that new the exception and return it. For example:
class File {
string fileName;
public byte[] Read(int bytes) {
if (!ReadFile(handle, bytes))
throw NewFileIOException();
}
FileException NewFileIOException() {
string description = // build localized string, including fileName
return new FileException(description);
}
}
Another alternative is to use the constructor of the exception to build the exception. This is more appropriate for global exception classes like ArgumentException.
- Do throw Exceptions in favor of returning an error code (or HResult)
- Do throw the most specific exception possible
- Do favor using existing exceptions over creating new ones
- Do set all the fields on the exception you use
- Do use Inner exceptions (chained exceptions).
- Do set meaningful message text targeted at the developer in the exception.
- Don’t have methods that throw NullReferenceException or IndexOutOfRangeException.
- Do argument checking on protected (family) members. Clear state in the documentation if the protected method does not do argument checking. Unless otherwise stated, assume argument checking is done. There may be performance gains in not doing argument checking.
- Do cleanup intermediate results when throwing an exception. Callers should be able assume that there are no side-effects when an exception is thrown from a function.
Example: If Hashtable.Insert throws an exception, then the caller can assume that the item was not added to the Hashtable (the hashtable implementation breaks this rule, but that's another issue).