Click here to Skip to main content
15,890,282 members
Articles / General Programming / Exceptions

Throwing Exceptions

Rate me:
Please Sign up or sign in to vote.
4.86/5 (5 votes)
16 Apr 2013LGPL35 min read 14.4K   8   5
Throwing exceptions

Throwing exceptions is in itself trivial. Just invoke throw with an exception object. But when should you throw? Should you create a new exception or rethrow an old one? What should you include in the exception information?

  1. What are exceptions?
  2. Designing exceptions
  3. Throwing exceptions

In the previous two articles, you’ve got to know what exceptions are and how to design them. This time I’ll go through some typical scenarios and show you how to throw exceptions.

As I’ve written earlier, exceptions should be thrown when something exceptional has happened. That is, if you expect something to be a certain way, it’s exceptional if it isn’t.

Validating Arguments

The most simple example is when you expect method arguments:

C#
public void PrintName(string name)
{
     Console.WriteLine(name);
}

In this case, you EXPECT that a correct name is supplied, as it’s the only way to ensure that the method works as expected. The code above will work with <c>null as an argument, but all you’ll see is an empty line. The problem is that you won’t detect that something went wrong (other than the empty line). The worst thing is that those kind of errors are hard to detect as from the code’s perspective it seems to work, while from the users perspective they do not get the expected output.

Now imagine that error in a production system where the business logic (“BL-A”) comes to an incorrect conclusion thanks to invalid arguments. That in turn will probably surface somewhere else in your application. For instance, where the generated result is used in some other business logic (“BL-B”). If you are really unlucky, your users will notice that “BL-B” doesn’t work as expected in some cases. That will lead to several hours of debugging (and probably adding a lot of logging) before discovering that it’s actually “BL-A” that didn’t get the expected arguments.

I would at any time choose to spend some extra time on making sure that I get reasonable values into my method arguments instead of having to fix business logic bugs later.

Let’s add validation:

C#
public void PrintName(string name)
{
	if (name == null) throw new ArgumentNullException("name");
	
     Console.WriteLine(name);
}

Now we get an exception directly instead of deeper down in the business logic. That check might be trivial, but without it, we would probably get the dreaded NullReferenceException somewhere deeper down in the call tack.

In this case, the name should also contain a specific length, and may only contain alpha numerics:

C#
public void PrintName(string name)
{
	if (name == null) throw new ArgumentNullException("name");
	if (name.Length < 5 || name.Length > 10) 
    throw new ArgumentOutOfRangeException("name", name, "Name must be between 5 or 10 characters long");
	if (name.Any(x => !char.IsAlphaNumeric(x)) 
    throw new ArgumentOutOfRangeException("name", name, "May only contain alpha numerics");
	
    Console.WriteLine(name);
}

If you find yourself repeating that code, you could create either a Validator method on the Person class or create a PersonName class and do the validation in it. It depends on your application type and how serious you think the invalid argument is.

I personally do sanity checks in all of my methods, but only do business validations in those methods that do business logic.

You Failed the Code

The other time to throw exceptions is when a method can’t deliver the expected result. A controversial example is a method with a signature like this:

C#
public User GetUser(int id)
{
}

It’s quite common that methods like that return <c>null when the user is not found. But is it really the correct way of doing so? I would say NO. I got three reasons to that.

First, the method is called GetUser() and not TryGetUser() or FindUser(). We are saying that we SHOULD get an user. Not delivering a user is therefore an exceptional case.

Second, we have somewhere got an ID for an user. And if that ID is not found in the data source, something is probably wrong.

Third, in most scenarios, we do expect to find an user. Hence it’s an exceptional case if we do not find one. By using null as a return value, we do communicate that we expect the user to be not found and not that it’s something exceptional.

The null Cancer

By using null, we have to have this code everywhere:

C#
var user = datasource.GetUser(userId);
if (user == null)
    throw new InvalidOperationException("Failed to find user: " + userId);
// actual logic here

That kind of code spreads like cancer when you allow null as a return value.

Why don’t we just use:

C#
public User GetUser(int id)
{
    if (id <= 0) throw new ArgumentOutOfRangeException
       ("id", id, "Valid ids are from 1 and above. Do you have a parsing error somewhere?");
   
    var user = db.Execute<User>("WHERE Id = ?", id);
    if (user == null)
        throw new EntityNotFoundException("Failed to find user with id " + id);
	   
    return user;
}

Now we get more DRY code since we don’t have to make sure that we get a user back. We can instead rely on the global exception handling to display the error message to the user.

Simply think twice by deciding that returning null is OK. In most cases, an exception is to prefer.

Context Information

If you take the example with GetUser(), you’ll see that the EntityNotFoundException is quite worthless without getting the ID. If you want to switch to better exception handling (i.e., not being afraid of throwing exceptions in all exceptional cases), you’ll also have to make sure that the exception messages are meaningful.

“Meaningful” means that you with the help of the exception message can prevent the exception in the future. That means that the future bug fix should be made in the method that provides the invalid user ID and not the GetUser(), as the latter would be a workaround and not a bug fix.

Rethrowing Exceptions

To me, rethrowing exceptions is just a useful technique to add additional context information:

C#
public void Query(string sql)
{
    if (sql == null) throw new ArgumentNullException("sql");
	if (!sql.Contains("SELECT")) throw new FormatException
       ("Expected SQL to be a SELECT query. You specified: " + sql);
	
	try
	{
	    using (var cmd = _connection.CreateCommand())
		{
		    cmd.CommandText = sql;
			using (var reader = cmd.ExecuteReader())
			{
			    return ToList(reader);
			}
		}
	}
	catch (DataException err)
	{
	    //ALWAYS include the original exception
	    throw new QueryException(sql, err);
	}
}

If the ADO.NET exceptions would have had enough information, rethrowing would have not been necessary.

If you catch one exception and throw another one, always make sure to add the original exception as the inner one as shown in the above example.

Catch/Log/rethrow

Some use catch/log/rethrow.

C#
public void Query(string sql)
{
    if (sql == null) throw new ArgumentNullException("sql");
	if (!sql.Contains("SELECT")) throw new FormatException("Expected SQL to be a SELECT query. 
        You specified: " + sql);
	
	try
	{
	    using (var cmd = _connection.CreateCommand())
		{
		    cmd.CommandText = sql;
			using (var reader = cmd.ExecuteReader())
			{
			    return ToList(reader);
			}
		}
	}
	catch (Exception err)
	{
	    _logger.Error("Failed", err);
		throw;
	}
}

DON’T.

ALL different application types in .NET have places where you can catch unhandled exceptions to log them. Simply only rethrow exceptions if there is a real reason to do so.

Do Not Rethrow Caught Exceptions

The callstack is empty when you create a new exception. It’s generated as the exception travels up the call stack.

That means that every time you throw an exception, the call stack is rebuilt as the exception travels up.

The following code will therefore result in a rebuilt callstack.

C#
try
{
   // [...]
}
catch (Exception err)
{
    // do something
    throw err;
}

If you want to keep the original callstack, you have to either use some hacks or wrap the exception with a new exception. A typical example in the .NET Framework is when you invoke a method using reflection. If an exception occurs, it’s wrapped with the TargetInvocationException.

Summary

Let’s summarize what we have learned:

  1. Use the method definition to determine what’s exceptional. If it’s unclear: Is the method/argument(s) naming as good as you think?
  2. Validate the method contract and always throw if it’s violated.
  3. Always throw if the method can’t deliver the expected result (try to avoid returning <c>null if possible).
  4. Try to include context information in the exceptions when throwing instead of using extensive logging.
This article was originally posted at http://blog.gauffin.org/2013/04/throwing-exceptions

License

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


Written By
Founder 1TCompany AB
Sweden Sweden

Comments and Discussions

 
GeneralMy vote of 4 Pin
LostTheMarbles22-Apr-14 4:47
professionalLostTheMarbles22-Apr-14 4:47 
GeneralRe: My vote of 4 Pin
jgauffin22-Apr-14 5:01
jgauffin22-Apr-14 5:01 
GeneralMy vote of 5 Pin
brother.gabriel30-Apr-13 5:51
brother.gabriel30-Apr-13 5:51 
QuestionMy 5 Pin
PrafullaVedante25-Apr-13 2:21
PrafullaVedante25-Apr-13 2:21 
GeneralMy vote of 5 Pin
lambertje17-Apr-13 10:35
lambertje17-Apr-13 10:35 

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.