Hero image credit: Photo by Pixabay

We had an interesting discussion in a team meeting today and a good point was brought up about try/catch blocks and what happens when you throw an error in the catch section that perhaps a lot of developers new to C# might not be aware of.

Let’s take a look at an example.

try {
  // some code
} catch (Exception ex) {
  // do something
  throw new Exception(ex);
}

In C#, when an exception is thrown, there are a lot of details associated with an exception, especially a lot of call stack details, that get attached to the exception itself. Those details can be very useful for figuring out what happened and why. However, in this example, you might think that the “throw new Exception(ex)” line will just pass the details on up the call chain. You would be wrong. When you do a “throw new”, it tosses out the current exception’s details and creates a new exception. Even if you reference the existing exception (“ex” in this case), all of the important details get lost other than the general exception message itself.

If you want to preserve those details for a catch block further up the chain to have and be able to reference, use the following code:

try {
  // some code
} catch (Exception ex) {
  // do something
  throw;
}

Use just the word “throw”. Nothing else. This will pass the original exception instance (along with all of its details) on up the chain to the next higher catch level.

Order Matters

Another thing newbies might not understand. In C#, exception catch blocks should be ordered from most specific to least specific. Take the following example:

try {
  // some code
} catch (IOException ex) {
  // do something
} catch (FileNotFoundException fnfex){
  // do something
}

In this case, the FileNotFoundException will never, ever be called, even if that was the actual type of the exception. The order of your catch blocks matters. If you have an exception type that inherits from another exception type, the catch blocks will consider them the same and go with whichever exception type matches first. In this case, FileNotFoundException inherits from IOException, which inherits from SystemException, which inherits from Exception. Because IOException also matches the type of FileNotFoundException, the IOException block will trigger first, since it’s the first in the order of catch blocks. So remember, the order matters.

You should always try to plan for the most specific exception types your block of code might encounter. This is important because the more specific exception classes will have details that a more generic exception type that it inherits from might not include. And those extra details could mean all the difference when debugging an issue. For example, the FileNotFoundException will include the FileName property, which IOException will not.

Fun Fact

Here’s one that used to be the case in Visual Studio & C#. When debugging, if you used the same variable name for each of the try/catch blocks, like this:

try {
  // some code
} catch (FileNotFoundException ex){
  // do something
} catch (IOException ex) {
  // do something
} catch (Exception ex){

}

it would cause a problem where when you looked at the value of ex in the debugger watch list, it would always be of the last type (in this case Exception) no matter which catch block you were stepping through because all of the catch blocks somehow were in the same scope, so last one wins. At least, as I recall that was the case. I don’t think that’s the case anymore, but it got me into the habit of always making sure that if I had multple catch blocks, I always used a different variable name for the exception in that block. Like this:

try {
  // some code
} catch (FileNotFoundException fnfex){
  // do something
} catch (IOException ioex) {
  // do something
} catch (Exception ex){

}