Click here to Skip to main content
12,632,113 members (26,797 online)
Click here to Skip to main content
Add your own
alternative version


27 bookmarked

The art of creating exceptions

, 6 Dec 2010 LGPL3
Rate this:
Please Sign up or sign in to vote.
This will be my final piece on exception handling. It's been fun, but the show must go on, right? Got any suggestion on what I should rant about in this blog? Leave a comment. Continue reading →

Please take a moment and think about WHY you throw exceptions.

You done? Have you come up with a reason? You are probably thinking: “That b!@tch is a total moron. I throw exceptions to know that something exceptional has happened!”. If that is the case, please stop reading here. (The first part of the thought is totally fine by me, I can be a total moron.)






No? Well. Ok then. Want to be an exception master? Got some ninja skills? Have you bought a jump suit? I have actually created a diploma (yes, It’s true) that I will email to anyone who leaves a comment that says: “I throw exceptions to be able to try to prevent them in the future”. Because that’s what you should really try to do. Sure, some exceptions that are thrown CAN’T be prevented. But a lot of them can.

Let’s take FileNotFoundException an example. It can in MOST cases be prevented, if you are not a lazy SOB, by checking if the file exists before trying to open it. The exception can STILL be thrown if the file is deleted between the check and you opening it. Life is harsh, isn’t it.

The problem is that if you do not provide any contextual information, you will have a hard time trying to prevent it in the future.

Instead of just writing:

throw new FileNotFoundException("Ohhh, the file was missing! *buhu*");

do write:

throw new FileNotFoundException(string.Format("Failed to find '{0}'.", amazingFileName));

Even better would have been if you could add some more context and add the filename as a parameter:

throw new FileNotFoundException(string.Format
  ("User photo for user #{0} was not found", userId)) { Filename = amazingFilename };

That gives a lot more information to work with, don’t you think? When designing exceptions, you should ask yourself “What kind of context information can I provide in this exception to make it easier to prevent it?”. Regarding a file, it’s quite obvious. Hence I would design the exception like this:

public class FileNotFoundException : IOException
      public FileNotFoundException(string message, string fileName)
	     : base(message)
		Filename = fileName;

        // *ALWAYS* include a constructor which takes an inner exception.
       public FileNotFoundException(string message, string fileName, Exception inner)
		: base(message, inner)

      public string Filename { get; private set; }

Let’s look at another example.

I’ve talked about a DataSourceException previous posts. It could be used to report problems in the data layer (regardless of the type of data source). The first thing that comes into my mind is that we have a target and some parameters. When using a web service, the target is the URI and the parameters are anything posted. Likewise, for SQL, the target is a SQL query and the parameters are the parameters used in the SQL query. Doing it like this creates a potential security risk. Do NOT expose exception details for the end users, never ever.

public class DataSourceException : Exception
	public DataSourceException
	(string message, string target, Dictionary<string, object> parameters)
	     : base(message)
			Filename = fileName;

         public FileNotFoundException(string message, Exception inner, 
		string target, Dictionary<string, object> parameters)
		: base(message, inner)

	public string Target { get; private set; }
	public Dictionary<string, object> Parameters { get; private set; }

How it can be used for a webservice request:

	var user = myWebSvc.GetUser(userId);
	if (user == null)
	  throw new InvalidOperationException
	(string.Format("Failed to find user #{0}", userId)); //kilroy was here
catch (WebException err)
    throw new DataSourceException(string.Format
	("Failed to download user.", userId), myWebSvc.Uri, userId);

Or for a database:

string errMsg = "Failed to update user";
    ExecuteCommand(cmd => {
	cmd.CommandText = "UPDATE user SET name=@name WHERE id=@id";
	cmd.AddParameter("name", name);
	cmd.AddParameter("id", userId);
}, errMsg);

Where did all code go? It’s a small method looking like this:

public void ExecuteCommand(Action<IDbCommand> action, string errMsg)
	// CreateConnection() uses DbProviderFactory and
	// throws DataSourceException if connection fails
	using (var connection = CreateConnection())
		var cmd = connection.CreateCommand();
		catch (DbException err) //notice that I only handle DbException?
			// WTF is this? Check next code snippet.
			// note that I *return* an exception from 
                       	// the extension method (instead of throwing it)
			throw cmd.ToException(errMsg, err);

I used a small extension method:

public static class DataExtensions
	public static Exception ToException(this IDbCommand command, 
			string errMsg, Exception inner)
		var parameters = new Dictionary<string, object>();
		foreach (var p in command.Parameters)
			parameters.Add(p.ParameterName, p.Value);
		throw new DataSourceException(errMsg, inner, 
			command.CommandText, parameters);


You should be fine if you stop to think “I throw exceptions to inform that something exceptional happens” and instead start thinking “I throw an exception to help try to prevent the exceptional cases from happening in the future”. Having that mindset helps you create much more detailed exception classes (and hopefully also provide that information).

Each time you are about to throw an exception, ask yourself: What information can I provide in this method to make it easy to find out why the exception was thrown? It might take a couple of minutes longer, but how long does it take to debug your application if you do NOT get that information? Usually a lot longer.

Action points for you:

  1. Create exception classes containing as much context information as possible
  2. Always create a constructor that takes an inner exception
  3. Throw exceptions to help prevent them in the future
  4. Try to include as much context information as possible

Share and Enjoy:PrintDiggStumbleUpondel.icio.usFacebookYahoo! BuzzTwitterGoogle BookmarksDZoneLinkedInSlashdotTechnorati


This article, along with any associated source code and files, is licensed under The GNU Lesser General Public License (LGPLv3)


About the Author

Founder Gauffin Interactive AB
Sweden Sweden
Founder of OneTrueError, a .NET service which captures, analyzes and provide possible solutions for exceptions.

blog | twitter

You may also be interested in...


Comments and Discussions

GeneralThanks for the article Pin
Plamen Velikov3-Aug-11 3:38
memberPlamen Velikov3-Aug-11 3:38 
GeneralCopyPasteException Pin
sjelen15-Dec-10 1:20
membersjelen15-Dec-10 1:20 
GeneralRe: CopyPasteException Pin
jgauffin15-Dec-10 3:08
memberjgauffin15-Dec-10 3:08 
GeneralNice article Pin
Lokanta_brahmachari13-Dec-10 5:14
memberLokanta_brahmachari13-Dec-10 5:14 
GeneralFileNotFoundException Pin
Richard Deeming10-Dec-10 9:42
memberRichard Deeming10-Dec-10 9:42 
GeneralRe: FileNotFoundException Pin
jgauffin13-Dec-10 10:21
memberjgauffin13-Dec-10 10:21 
GeneralMy vote of 4 Pin
vdasus8-Dec-10 5:39
membervdasus8-Dec-10 5:39 
GeneralMy vote of 5 Pin
dpflovely7-Dec-10 23:11
memberdpflovely7-Dec-10 23:11 
GeneralAbout those lazy SOBs who throw FileNoFoundException spelling exceptions Pin
clintonG6-Dec-10 7:12
memberclintonG6-Dec-10 7:12 
GeneralRe: About those lazy SOBs who throw FileNoFoundException spelling exceptions Pin
rustylee20056-Dec-10 7:18
memberrustylee20056-Dec-10 7:18 
GeneralRe: About those lazy SOBs who throw FileNoFoundException spelling exceptions Pin
jgauffin6-Dec-10 10:24
memberjgauffin6-Dec-10 10:24 
GeneralSome tid bits... Pin
Andrew Rissing6-Dec-10 5:19
memberAndrew Rissing6-Dec-10 5:19 
GeneralRe: Some tid bits... Pin
jgauffin6-Dec-10 9:17
memberjgauffin6-Dec-10 9:17 
GeneralRe: Some tid bits... Pin
Andrew Rissing6-Dec-10 14:58
memberAndrew Rissing6-Dec-10 14:58 
Generalnice Pin
Rozis3-Dec-10 14:19
memberRozis3-Dec-10 14:19 
GeneralRe: nice Pin
jgauffin6-Dec-10 10:29
memberjgauffin6-Dec-10 10:29 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.161208.2 | Last Updated 6 Dec 2010
Article Copyright 2010 by jgauffin
Everything else Copyright © CodeProject, 1999-2016
Layout: fixed | fluid