Saturday, October 13, 2012

Handling exceptions


Exceptions, exceptions, exceptions.... like Steve Ballmer once said: developers, developers, developers...

So, if you are working with C# you probably heard a lot about exceptions.

First of all, I would like to say that working with exceptions is hard. To handle them you first need to understand why they are invented at first place. If you Google about this question you’ll find out that this goes way back before the first modern languages were invented (you can look at this thread at stackoverflow about historical facts).

So, why are exceptions invented?

In short, they are used to signal that program is in an unexpected state.

What does it mean?

Every programmer knows that sooner or later he will be facing with unexpected problem on which he didn't count. But detecting these situations could be very hard and expensive - error is encountered on every 10th iteration, user has lost data until he finally noticed that something went wrong, etc.

But, let’s write some small example:
           
bool ExecuteSql(string sql)
{
       if (!OpenConnection() &&
    !ParseQuery(sql) &&
    !Execute(sql))
       {
              return false;
       }

       CloseConnection();
}

void DoTheJob()
{
       ExecuteSql("INSERT INTO USERS VALUES ('John')");
       ExecuteSql("UPDATE INTO PERMISSIONS VALUES ('John', 'Read')");
       ExecuteSql("UPDATE INTO PERMISSIONS VALUES ('John', 'Write')");
       ExecuteSql("UPDATE INTO PERMISSIONS_DETAILS VALUES ('John', 'Write', 'CUSTOMERS')");
}

In this little example we have two methods:  ExecuteSql – used to execute sql on remote server;  DoTheJob – used to execute some business logic.
In DoTheJob method we are inserting data into three different tables. These tables are in the following relationship:


We have three tables and each has its own PK but PERMISSONS has also FK constraint declared on USERS PK and PERMISSONS_DETAILS has FK constraint on PERMISSONS PK. Our business logic says that in USERS table we add users, in PERMISSIONS table we add permissions fro that user and it could be read/write permission. In our third table PERMISSIONS_DETAILS we add more detailed info about permissions (e.g. which table could be read/write by some user).

Let’s start examine DoTheJob method. In this method we are inserting data into all tables but we must insert it in specific order (remember we have declared FK constraints). First, we insert into the USERS table, then into the PERMISSONS and then into the PERMISSIONS_DETAILS. Every dissent programmer will notice one problem here – we assumed that inserting user ‘John’ is valid operation that could never fail, which is clearly wrong and it would lead to domino effect if this first insert fails. So, let’s first define that in our pseudo code, pseudo C# language we don’t have exceptions. We’ll pretend that we don’t know anything about them and thus we will need to figure out how to handle errors.
If we look at ExecuteSql method we will see that it returns Boolean – we can leverage this to handle if anything goes wrong with execution to abort insertions. So we can rewrite DoTheJob as following:

void DoTheJob()
{
       if (ExecuteSql("INSERT INTO USERS VALUES ('John')"))
       {
              ExecuteSql("UPDATE INTO PERMISSIONS VALUES ('John', 'Read')");

              if (ExecuteSql("UPDATE INTO PERMISSIONS VALUES ('John', 'Write')"))
              {
                     ExecuteSql("UPDATE INTO PERMISSIONS_DETAILS VALUES ('John', 'Write', 'CUSTOMERS')");
              }
       }
}


Now this code looks robust, but we can notice that it is also much harder to read it than before. So, imagine that we have many of these calls to ExecuteSql method, in major cases we expect that it is going to execute without any errors, but in some cases it will fail. Now, we would like to have some kind of mechanism that will provide us exactly that – we can call any method without checking its result (because we rarely except it to fail) but if something goes wrong this mechanism will inform us – in other words, exceptions.

Exceptions are…. exceptions, what else. It is something which we don’t expect normally, but if this unexpected situation occurs we must know about it and we must somehow handle it.
But, wait; we still don’t know how to implement these special, unexpected situations. So, we try for ourselves to implement something and we write something like this:

delegate void ErrorHandler();

void ExecuteSql(string sql, ErrorHandler handler)
{
       if (!OpenConnection() ||
              !ParseQuery(sql) ||
              !Execute(expression))
       {
              handler();
              return ;
       }

       CloseConnection();
}

void DoTheJob()
{
       ErrorHandler handler = delegate()
       {
              MessageBox.Show("Error occurred user not inserted.");
       };

       ExecuteSql("INSERT INTO USERS VALUES ('John')", handler);
       ExecuteSql("UPDATE INTO PERMISSIONS VALUES ('John', 'Read')", handler);
       ExecuteSql("UPDATE INTO PERMISSIONS VALUES ('John', 'Write')", handler);
ExecuteSql("UPDATE INTO PERMISSIONS_DETAILS VALUES ('John', 'Write', 'CUSTOMERS')", handler);
 }

Now we have defined a new delegate which we pass to ExecuteSql method and it will be called if anything goes wrong. If we try this mechanism we will notice something strange, although we don’t need to check for return value and we’ll be still informed if something goes wrong, we are still executing next insert statement!

So, to overcome this problem we need to deal with continuation, we must say to compiler “Hey! Stop executing these set of instructions and go to the next one”. But wait, we don’t have any set defined, which set of instructions do we want to skip?
OK then, we can rewrite it again:

void DoTheJob()
{
       ErrorHandler handler = delegate()
       {
              MessageBox.Show("Error occurred user not inserted.");
       };
      
{
              ExecuteSql("INSERT INTO USERS VALUES ('John')", handler);
              ExecuteSql("UPDATE INTO PERMISSIONS VALUES ('John', 'Read')", handler);
              ExecuteSql("UPDATE INTO PERMISSIONS VALUES ('John', 'Write')", handler);
              ExecuteSql("UPDATE INTO PERMISSIONS_DETAILS VALUES ('John', 'Write', 'CUSTOMERS')", null);
}
}

Here, we have added additional curly braces to mark the set of instructions that we want to be skipped, so if anything goes wrong with any of the ExecuteSql calls, rest of the calls must be skipped. We must exit from method, because nothing more is left to execute. Note: in case that we had some instructions under the block - that would be our next instruction to execute not returning from method.

So, now we have defined which set of instructions should be skipped if anything goes wrong, but to implement this mechanism we need to deal, as I said previously, with continuation, or we can incorporate this exactly into the compiler because we can control more easily continuation and we can construct various branching solutions. And that’s exactly where exceptions are incorporate – in C# we define which block should be called when something goes wrong and also which block should be terminated if something goes wrong (if you want to read more on .NET, please visit this link http://blogs.msdn.com/b/cbrumme/archive/2003/10/01/51524.aspx but even before you read this you should read about SEH mechanism http://www.microsoft.com/msj/0197/exception/exception.aspx).

Now we know why exceptions are invented – we have mechanism that will always inform us if anything goes wrong.

But after some time we found out that dealing with exception is hard and that we always have to take care of two things:
  •           Catching existing exceptions
  •           Throwing your own exceptions

Let start with throwing exceptions – say we have our API and we want to throw exception because of some error we got from our internal business logic. The most important question to ask yourself is – is it a really rare situation that will rarely happen? If it is not, you should return some error value instead. Remember, exceptions must be used to signal that something is wrong, in major cases that shouldn't be handled at all – we either have wrong architecture design or we have external dependency on which we can’t affect (e.g. out of memory exception).

Now, let’s see how to catch exceptions.
The most important question to ask yourself is – do I know what to do with this exception, do I really expect to see this exception?
I've seen many cases when people are catching exceptions just because some method declared that it throws some exceptions.

The best example of this is this one:

try
{
       string s1 = null;
       string s2 = string.Empty;
       File.Copy(null, string.Empty);
}
catch (ArgumentException) { }
catch (ArgumentNullException) { }
catch (PathTooLongException) { }


This is WRONG! You should check for arguments, you don’t need to catch any of these exceptions. But OK, this is fairly simple and obvious example.

Let’s see another one more difficult to recognize:

       ComboBox _combobox = new ComboBox();

       void f()
       {
              _combobox.SelectedIndexChanged += new EventHandler(_combobox_SelectedIndexChanged);
              _combobox.Items.Add(new MyClass());
              _combobox.Items.Add(new MyClass());
       }

       void  _combobox_SelectedIndexChanged(object sender, EventArgs e)
       {
              try
              {
                     ((MyClass)_combobox.SelectedItem).Name = "Custom text";
              }
              catch
              {
              }
       }

You don’t need catch block for casting. Yes, SelectedItem is of type “object” and it could be anything if you just examine property per-se, but in this context, it is of type MyClass, and it will always be, this is compile time resolution not run-time!

Someone could say – well, this is not safe; you should use generic classes instead. I will agree, but in some cases you don’t have generic classes as in this example and in these cases you should always analyze your context, don’t swallow your exceptions.
But what about next one:

       void MethodA()
       {            
              MethodB("apple");         
       }

       void MethodB(string s)
       {
              try
              {
                     MethodC(s);
              }
              catch (MyException)
              {
                     // It's ok, we expect this exception
                     // will try to call once again this method but with better parameters
                     MethodC("12345");
              }
       }

       void MethodC(string s)
       {
              if (s.Length < 5)
              {
                     throw new MyException();
              }

              if (s.StartsWith("*"))
              {
                     throw new MyOtherException();
              }

              // Do Stuff
       }

We have three methods: MethodA, MethodB and MethodC. We call from MethodA -> MethodB and from MethodB -> MethodC. When calling MethodB we don’t expect any exception but when calling MethodC we can see that it has two exceptions that it can throw: MyException and MyOtherException (please ignore for now how did we concluded that this method can throw two exceptions – in C# we can do that from xml comments and in more painful way with .NET reflector or any similar tool, unfortunately we don’t have checked exceptions in C# L). But as from MethodB’s point of view, it is only interested in MyException. We don’t expect for some reason that MyOtherException is ever thrown. So, we test our example and it all works OK, but suddenly when we pass to MethodA string like “*pple” it fails with exception:

       public void MethodA()
       {
              try
              {
                     MethodB("*pple");
              }
              catch
              {
                     // Ignore exceptions
              }
       }

What should we do – catch general exception or try to figure out what is going on?
It depends – in major cases we must find out what is going on, but in some cases we are “allowed” to catch exceptions.

When we mustn't catch general exception:
  •           When this is our code – we have the source code for all the methods that are involved in hierarchical calling tree
  •           In case when this isn't our code but we can use preconditions to avoid exceptions (like example with ArgumentException)

When we can catch general exception:
  •           When it is 3rd party API and we don’t want our code to crash
  •      When we have "mission critical" code to execute and we must provide robust design, in this case it is not important whether it is 3rd API or our internal code
  •           When it is top-most exception for thread – we can use some custom logic to inform user about problem that has occurred and we can try to recover somehow or we can just terminate the application or just that thread

This is the major problem with exceptions – it depends on context what should be done. In major cases we can say that catching exceptions should be avoided as much as it is possible, catching general exception even more but as we can see it really depends on the context, so you must analyze for yourself every time you need to throw or to catch the exception.

No comments:

Post a Comment