I have the below code:
public void OpenFile(string FileName)
{
if (FileName == null)
throw new ArgumentNullException("FileName", "OpenFile: Filename is null");
List<int> readItems = new List<int>();
using (StreamReader reader = new StreamReader(FileName))
{
string line;
int batchItem;
while ((line = reader.ReadLine()) != null)
{
if (int.TryParse(line, out batchItem))
{
readItems.Add(batchItem);
}
}
}
CurrrentFile = FileName;
FileInfo f = new FileInfo(FileName);
lock (LockObject)
{
TextWriter = f.AppendText();
TextWriter.AutoFlush = true;
}
if (readItems.Count > 0)
FileOpened(readItems);
}
I am trying to detect possible problems eg/ Filename is null.
In the class that catches and logs exceptions I obviously have a catch(ArgumentNullException ex)
Should I also be catching possible exceptions thrown by the StreamReader constructor and FileInfo constructor?
I know that sounds silly but I wondered whether I should have explicit catches for the exceptions I’m throwing and then a general exception catch whether it be a catch(Exception ex) or have a try catch around the above code and rethrow a custom exception. Otherwise my try/catch block has about 12 separate catch statements!
In general if you follow a few simple rules you can drastically simplify your Exception Handling code.
1. Only catch and handle exceptions you can actually do something about
If you can’t actually recover from an exception, then you shouldn’t catch it at all. Let the exception bubble up to the highest possible point (usually the User Interface).
When you do catch exceptions you should catch very specific exceptions, and avoid generic exception handling if at all possible.
There are cough exceptions to the rule above, but that brings us to our second rule.
2. Only use generic exception handling for logging, and implementation hiding
Since you should be letting your exceptions bubble all the way up to the highest layer, anything that makes it to the top is a bug. At that point, you should be logging that exception and presenting some friendly message to the user.
Another place you might want to do this is in order to maintain a contract for an API, and hide implementation details. If the end user is calling a method, they may not need to know every possible problem that occurred, only that it didn’t work.
3. Avoid Exceptions Like The Plague!
Exceptions should be, well… exceptional! In most instances you should be able to avoid exceptions by simply coding for them in advance. A real exception should represent something you couldn’t predict would happen. A simple example is checking for the existence of a file.
In that example, I knew it was possible the file would not exist… so I should have written code to make sure that case almost never happens.
Now, it is still possible for a
FileNotFoundExceptionto be thrown here, but how would we handle it? Clearly this code can no longer do anything about it since something truly EXCEPTIONAL happened. We can let the exception bubble up to the next layer since there is nothing meaningful we can do here.