It’s ok to throw System.Exception…
- by Chris Skardon
No. No it’s not. It’s not just me saying that, it’s the Microsoft guidelines: http://msdn.microsoft.com/en-us/library/ms229007.aspx Do not throw System.Exception or System.SystemException. Also – as important: Do not catch System.Exception or System.SystemException in framework code, unless you intend to re-throw.. Throwing: Always, always try to pick the most specific exception type you can, if the parameter you have received in your method is null, throw an ArgumentNullException, value received greater than expected? ArgumentOutOfRangeException. For example: public void ArgChecker(int theInt, string theString)
{
if (theInt < 0)
throw new ArgumentOutOfRangeException("theInt", theInt, "theInt needs to be greater than zero.");
if (theString == null)
throw new ArgumentNullException("theString");
if (theString.Length == 0)
throw new ArgumentException("theString needs to have content.", "theString");
}
Why do we want to do this? It’s a lot of extra code when compared with a simple:
public void ArgChecker(int theInt, string theString)
{
if (theInt < 0 || string.IsNullOrWhiteSpace(theString))
throw new Exception("The parameters were invalid.");
}
It all comes down to a couple of things; the catching of the exceptions, and the information you are passing back to the calling code.
Catching:
Ok, so let’s go with introduction level Exception handling, taught by many-a-university: You do all your work in a try clause, and catch anything wrong in the catch clause. So this tends to give us code like this:
try {
/* All the shizzle */
}
catch {
/* Deal with errors */
}
But of course, we can improve on that by catching the exception so we can report on it:
try { }
catch(Exception ex) { /* Log that 'ex' occurred? */ }
Now we’re at the point where people tend to go:
Brilliant, I’ve got exception handling nailed, what next???
and code gets littered with the catch(Exception ex) nastiness. Why is it nasty? Let’s imagine for a moment our code is throwing an ArgumentNullException which we’re catching in the catch block and logging. Ok, the log entry has been made, so we can debug the code right? We’ve got all the info… What about an OutOfMemoryException – what can we do with that?
That’s right, not a lot, chances are you can’t even log it (you are out of memory after all), but you’ve caught it – and as such - have hidden it.
So, as part of this, there are two things you can do one, is the rethrow method:
try { /* code */ }
catch (Exception ex)
{
//Log
throw;
}
Note, it’s not
catch (Exception ex)
{
throw ex;
}
as that will wipe all your important stack trace information. This does get your exception to continue, and is the only reason you would catch Exception (anywhere other than a global catch-all) in your code. The other preferred method is to catch the exceptions you can deal with.
It may not matter that the string I’m passing in is null, and I can cope with it like this:
try{
DoSomething(myString);
}
catch(ArgumentNullException){}
And that’s fine, it means that any exceptions I can’t deal with (OutOfMemory for example) will be propagated out to other code that can deal with it. Of course, this is horribly messy, no one wants try / catch blocks everywhere and that’s why Microsoft added the ‘Try’ methods to the framework, and it’s a strategy we should continue.
If I try:
int i = (int) "one";
I will get an InvalidCastException which means I need the try / catch block, but I could mitigate this using the ‘TryParse’ method:
int i;
if(!Int32.TryParse("one", out i))
return;
Similarly, in the ‘DoSomething’ example, it might be beneficial to have a ‘TryDoSomething’ that returns a boolean value indicating the success of continuing. Obviously this isn’t practical in every case, so use the ol’ common sense approach.
Onwards
Yer thanks Chris, I’m looking forward to writing tonnes of new code.
Fear not, that is where helpers come into it… (but that’s the next post)