Exceptions

Checking errors programmatically

Use if, when you want to check something programmatically. You want to check everything programmatically that can be considered a normal part of execution.

For example:

if ( lCurrentConnection.State != ConnectionState.Closed )
 {  
 lCurrentConnection.Close();
 }

or

if ( lMyVar != null ) 
{
 lMyVar.DoSomething(); 
} 
else 
{
 lMyVar = InitMyVar();  
 lMyVar.DoSomething();
}

Try-Catch

Use exception handling (try-catch) if the event doesn’t occur very often, that is, if the event is truly exceptional and indicates an error.

The try should contain the code, that potentially cause an exception, the catch should contain the handling of the exception, and you can use finally block to clean up resources, if necessary.

The finally statement will closes or reallocates resources whether or not an exception occurs.

In catch blocks, always order exceptions from the most specific to the least specific.

Don’t throw exceptions under normal circumstances. Return null or false for extremely common error cases instead of throwing an exception. An extremely common error case can be considered normal flow of control. By returning null or false in these cases, you minimize the performance impact to an app.

In most cases, use the predefined .NET Framework exception types. Introduce a new exception class only when a predefined one doesn’t apply.

Throw an InvalidOperationException exception if a property set or method call is not appropriate given the object’s current state. Throw an ArgumentException exception or a class derived from ArgumentException if invalid parameters are passed

Custom Exceptions

.
For example:

public class MyFileNotFoundException : Exception
 { ...  }

Use at least the three common constructors when creating your own exception classes: the default constructor, a constructor that takes a string message, and a constructor that takes a string message and an inner exception.

  • Exception() , which uses default values.
  • Exception(String), which accepts a string message.
  • Exception(String,Exception), which accepts a string message and an inner exception.

Use grammatically correct error messages, including ending punctuation. Each sentence in a description string of an exception should end in a period. For example, “The log table has overflowed.” would be an appropriate description string.

Provide Exception properties for programmatic access. Provide additional properties for an exception (in addition to the description string) only when there’s a programmatic scenario where the additional information is useful.
For example, the FileNotFoundException provides the FileName property.

Stack trace

The stack trace begins at the statement where the exception is thrown and ends at the catch statement that catches the exception. Be aware of this fact when deciding where to place a throw statement.

Use exception builder methods. It is common for a class to throw the same exception from different places in its implementation. To avoid excessive code, use helper methods that create the exception and return it.

For example:

public class FileReader 
{ 
private string lFileName;
public FileReader( string aPath) 
{   
lFileName = aPath; 
}

public byte[] Read( int aBytes )  
{ 
  byte[] lResults = FileUtils.ReadFromFile( lFileName, aBytes );

  if ( lResults == null )  
  {    
   throw NewFileIOException();   
  }
  return lResults;  
}

FileReaderException NewFileIOException() 
 {
string lDescription = "My NewFileIOException Description";   
return new FileReaderException( lDescription );  
 }
}

External Data

External data is not reliable. It must be extensively checked.

It doesn’t matter if the data is coming from the registry, database, from a disk, from a socket, from a file you just wrote or from the keyboard.

All external data should be checked and only then you can rely on it.

Anytime you need external data, you can have the following situations:

  • Not enough security privileges
  • The information is not there
  • The information is incomplete
  • The information is complete, but invalid

Don’t throw new Exception()

Exception is a too broad class, and it’s hard to catch without side-effects.

Generic Exceptions caught should be published

If you caught a generic Exception, log it somewhere. But log it only once – often code is ridden with catch blocks that log exceptions and you end up with a huge log, with too much repeated information to be useful.

Log Exception.ToString(); never log only Exception.Message

You should always log Exception.ToString(), and never Exception.Message.

Exception.ToString() will give you a stack trace, the inner exception and the message.

Often, this information is priceless and if you only log Exception.Message, you’ll only have something like “Object reference not set to an instance of an object”.

Good code throws exceptions as needed, and handles only the exceptions it knows how to handle

Too generic exception handler can cause some problems.

For example:

public class MyClass
{
 public static string ValidateNumber( string aUserInput )
 {
  try
  {
   int lUserInputConvertedInt = GenericLibrary.ConvertToInt(userInput);
   return "Valid number";
  }
  catch (Exception)
  {
   return "Invalid number";
  }
 }
}

public class GenericLibrary
{
 public static int ConvertToInt( string aUserInput )
 {
  return Convert.ToInt32( aUserInput );
 }
}

According to the MSDN documentation, Convert.ToInt32 only throws ArgumentException, FormatException and OverflowException. So, those are
the only exceptions that should be handled.
If the GenericLibrary assembly is not included, FileNotFoundException can happen when the ConvertToInt was called, and the code will assume
that it’s because the number is invalid.

Don’t ever swallow exceptions

The worst thing you can do is catch (Exception) and put an empty code block on it. Never do this.

Cleanup code should be put in finally blocks

Never do cleanup code, e.g., closing streams, restoring state, closing or deleting file(s), outside of a finally block.
One thing that people often overlook is how a try/finally block can make your code both more readable and more robust.
As a sample, suppose you need to read some temporary information from a file and return it as a string.
No matter what happens, you need to delete this file, because it’s temporary.
This kind of return & cleanup begs for a try/finally block.
Let’s see the simplest possible code without using try/finally:

private string ReadTempFile(string aFileName)
{
 string lFileContents;
 using ( StreamReader lMyStreamReader = new StreamReader( aFileName ) )
{
 lFileContents = lMyStreamReader.ReadToEnd();
}
 File.Delete( aFileName );
 return lFileContents;
}

This code also has a problem when an exception is thrown on, e.g., the ReadToEnd method: it leaves a temporary file on the disk. Now, see how much cleaner and robust is the try/finally solution:

private string ReadTempFile(string aFileName)
{
 try
 {
 using ( StreamReader lMyStreamReader = new StreamReader( aFileName ) )
  {
   return lMyStreamReader.ReadToEnd();
  }
 }
 finally
 {
  File.Delete( aFileName );
 }
}

Where did the lFileContents variable go? It’s not necessary anymore, because we can return the contents and the cleanup code executes after the return point.
This is one of the advantages of having code that can run after the function returns: you can clean resources that may be needed for the return statement.

Don’t use exception handling as means of returning information from a method

This is a bad design. Not only exceptions are slow (as the name implies, they’re meant only to be used on exceptional cases), but a lot of try/catch blocks in your code makes the code harder to follow. Proper class design can accommodate common return values. If you’re really in need to return data as an exception, probably your method is doing too much and needs to be split.

Use exceptions for errors that should not be ignored

If there is a step that is the absolute precondition for the rest of your test, use exception handling there.
For example, if you absolutely need to be logged in, and that is your first step: If Login fails, or is not called, every other method call will fail. So it’s probably best to throw an exception from the Login method if it fails, instead of simply returning false, so the calling program cannot ignore it.

Don’t clear the stack trace when re-throwing an exception

The stack trace is one of the most useful information that an exception carries. Often, we need to put some exception handling on catch blocks (e.g., to rollback a transaction) and re-throw the exception.

See the right (and the wrong) way of doing it:

The wrong way:

try
{
 // Some code that throws an exception
}
catch (Exception lException)
{
 // some code that handles the exception
 throw ex;
}

Why is this wrong? Because, when you examine the stack trace, the point of the exception will be the line of the “throw ex;”, hiding the real error location.

The right way:

try
{
 // Some code that throws an exception
}
catch (Exception lException)
{
 // some code that handles the exception
throw;
}

What has changed? Instead of “throw ex;”, which will throw a new exception and clear the stack trace, we have simply “throw;”.
If you don’t specify the exception, the throw statement will simply rethrow the very same exception the catch statement caught.
This will keep your stack trace intact, but still allows you to put code in your catch blocks.

Standard Exception Types

Exception type Base Type Description Example
Exception Object Base class for all exceptions. None (use a derived class of this exception).
SystemException Exception Base class for all runtime-generated errors None (use a derived class of this exception).
IndexOutOfRangeException SystemException Thrown by the runtime only when an array is indexed improperly. Indexing an array outside of its valid range:

lMyArray[lMyArray.Length+1]

NullReferenceException SystemException Thrown by the runtime only when a null object is referenced. object lMyObject = null; lMyObject.ToString();
InvalidOperationException SystemException Thrown by methods when in an invalid state. Calling Enumerator.GetNext()after removing an It emfrom the underlying collection.
ArgumentException SystemException Base class for all argument exceptions. None (use a derived class of this exception).
ArgumentNullException ArgumentException Thrown by methods that do not allow an argument to be null. String lMyString = null;

“OtherString”.IndexOf (lMyString);

ArgumentOutOfRangeException ArgumentException Thrown by methods that verify that arguments are in a given range string lMyString = “string”; lMyString.Chars[9];

Other good resources

  • https://www.codeproject.com/Articles/9538/Exception-Handling-Best-Practices-in-NET
  • https://msdn.microsoft.com/en-us/library/87cdya3t(v=vs.110).aspx
  • http://blog.devbot.net/conventions-exceptions/
  • http://catalogue.pearsoned.co.uk/samplechapter/0321130022.pdf
  • https://msdn.microsoft.com/en-us/library/ms229030(v=vs.110).aspx
  • https://msdn.microsoft.com/en-us/library/ms229007(v=vs.110).aspx
  • https://msdn.microsoft.com/en-us/library/ms229009(v=vs.110).aspx
  • https://blogs.msdn.microsoft.com/kcwalina/2007/01/30/how-to-design-exception-hierarchies/

Written by Dániel Erdős