Tuesday, February 19, 2013

Tips and Tricks of Exception Handling in .Net - Part 1

This week I was reading Exceptions and State Management chapter of the book CLR via C# and learnt some interesting stuffs. I did some experiment and summarized few points which I would like to share. OK, lets start them one by one.

Structured Exception Handling: The .Net Framework Exception Handling is built using this mechanism which is offered by Microsoft Windows. It mainly introduces 3 blocks namely try, catch and finally. 
Where,
  • try - is like a guard where we write the code which might cause an exception to be raised.
  • catch - is a block which is intended to handle an exception which is raised in try block.
  • finally - is a block which is intended to write a cleanup code.
Refer this link for more information regarding SEH.

What can be thrown?
       The CLR allows an instance of any type to be thrown. But CLS mandates to throw only the Exception derived objects. The C# compiler allows to throw only exception derived types.

Some points about try, catch, finally and throw in C#:
  • try cannot stand by itself. Means, try block musht be followed by catch or finally block.
  • You can write multiple catch blocks for a single try block but you cannot write multiple finally blocks.
  • You can write catch in 2 ways:
          1. Without specifying any exception type.
          2. By specifying an exception type.
  • finally  block always executes, even if try block is having a return statement. However, you cannot write return statement in this block!
The points which I mentioned above is the basic things and compiler itself teaches you if you are not aware of it. But there are some interesting things which is related to these points which I will address later on this post.

catch vs catch(Exception ex): These both are same and used to handle generalized exceptions.
The first one can be written like this:
try
{
}
catch
{
   throw; // Throws any and all exceptions without altering the stack trace.
}
and the later one can be written like this:
try
{
}
catch(Exception ex)
{
   throw; // Throws any and all exceptions without altering the stack trace.
}
The above two code fragments will be having same behavior(but you will get compiler warning for the second one, since the variable ex is not used in the block).

Note: This is same only if the CLR version is 2.0 and above - Prior to that the later one will catch only CLS-Compliant Exceptions. In version 2.0 of CLR, Microsoft introduced RuntimeWrappedException class which is derived from Exception so it is CLS-compliant exception type. This class contains a private field of type Object. In version 2.0 and above  of the CLR, when a non-CLS compliant exception is thrown the CLR automatically constructs the instance of RuntimeWrappedException and initializes its private field to refer to the object that was actually thrown.

When we need to log the exception information or do some other operations based on the values of exception object, then we can use the later one. Otherwise first one will be fine to delegate the exception to the caller.

Be Careful with throw vs throw ex

Now lets alter the second code like this:
try
{
}
catch(Exception ex)
{
   throw ex; // Throws any and all exceptions by altering the stack trace.
}
Note the throw statement above. This makes a lot of difference to the first code(i.e. throw;). When an exception variable  is specified along with the throw statement, the stack trace of the exception will be altered. Here we are lying the client code by changing the actual location of the exception! Hence, it will be difficult to debug such codes and therefor it is not the preferred or recommended way.

Here is an example code to check this behavior.

Throwing different exception than the catched one: We can also throw a different exception than the one which is catched by catch clause. This is useful when we need to maintain the meaning of a methods contract. Ex:
    
    // Source: CLR via C# by Jeffrey Richter
    internal sealed class PhoneBook
    {
        private string pathname;

        public string GetPhoneNumber(string name)
        {
            string phone;
            FileStream fs = null;
            try
            {
                fs = new FileStream(pathname, FileMode.Open);
                // Code to read from fs until the name is found goes here.
            }
            catch (FileNotFoundException ex)
            {
                // Throw different exception containing the name, and
                // set the originating exception as the inner exception.
                throw new NameNoteFoundException(name, ex);
            }
            catch (IOException ex)
            {
                // Throw different exception containing the name, and
                // set the originating exception as the inner exception.
                throw new NameNoteFoundException(name, ex);
            }
            finally
            {
                if (fs != null)
                    fs.Close();                
            }
        }
    }
As you can see in the above code GetPhoneNumber's contract is to find and return the phone number. Hence, in this case a meaningful exception will be NameNoteFoundException(Off-Course, this should be of type Exception derived).

We can see this approach in many places of FCL like type initialization, smtp connection, dealing with entity framework context etc.

Note: This should be used with extensive care. Improper usage of this will make the code horrible to maintain!

You can also add information to the exception variable before throwing it. Like: ex.Data.Add(anyObject);


Topics will be covered in Part - 2: CER(Constrained Execution Region) and Automatic Generation of try/catch/finally.

Read Part -2

No comments:

Post a Comment