Can following code be considered as a good practice? If not, why?
try
{
// code that can cause various exceptions.
Foreword: I consider this pattern to be usable only in certain conditions and with full understanding of its good and bad sides.
Statement 2: Sometimes we want from a library member to a) Get a result or b) Tell us it's impossible and we don't care about all the details, we'll just send details to the library developers.
Conclusion from St.1, St.2: When implementing a library method we can wrap a general Exception and throw a Custom one, including the source one as its InnerException. In this case a developer using this member will need to catch just one exception and we'll still get debug information.
public string GetConfig()
{
try
{
var assembly = Assembly.GetExecutingAssembly();
var resourceName = "MyCompany.MyProduct.MyFile.cfg";
// ArgumentNullException
// ArgumentException
// FileLoadException
// FileNotFoundException
// BadImageFormatException
// NotImplementedException
using (Stream stream = assembly.GetManifestResourceStream(resourceName))
// ArgumentException
// ArgumentNullException
using (StreamReader reader = new StreamReader(stream))
{
// OutOfMemoryException
// IOException
string result = reader.ReadToEnd();
}
return result;
// TODO: Read config parameter from DB 'Configuration'
}
catch (Exception ex)
{
throw new ConfigException("Unable to get configuration", ex);
}
}
It's a pretty solid code and you are sure it won't throw an exception ever. Because it's not supposed to. Are you sure? Or you'd wrap it into try-catch, just in case? Or you'd make a developer do this job? What if he doesn't care whether this method will succeed, may be he has a backup plan? May be he'll wrap call to this method to try-catch(Exception e) instead of you? I don't think so.
Pros:
InnerException
.Cons:
Exception
and I think this is important underestimated part of the Exception Handling facility.
catch (Exception ex)
{
ex.Data.Add(paramName);
throw;
}
Addition: I would edit your pattern in the following way:
try
{
// code that can cause various exceptions...
}
catch (Exception e)
{
if (e is ThreadAbortException || e is StackOverflowException || e is OutOfMemoryException)
{
throw;
}
throw new MyCustomException("Custom error message", e);
}
I think that this issue is very speculative, it often depends on specific case, but I did some research and I will share it. First, I want to express my opinion on code from lain Galloway:
try {
GetDataFromNetwork("htt[://www.foo.com"); // FormatException?
GetDataFromNetwork(uriArray[0]); // ArrayIndexOutOfBounds?
GetDataFromNetwork(null); // ArgumentNull?
}
catch(Exception e)
{
throw new WeClearlyKnowBetterException(
"Hey, there's something wrong with your network!", e);
}
If GetDataFromNetwork could throw FormatException, than this external calling should has own method, in which will be handled that exception and it should be converted into custom exception like here:
try {
GetDataFromNetwork();
} catch (FormatException ex) {
// here you should wrap exception and add custom message, which will specify occuring problem
}
When I´m creating custom exception for specific application, I extend MyGeneralException from Exception and every more specific exception will extend MyGeneralException. So, at the moment you are wrap into custom exception, you should then put in the method throws MyGeneralException.
I´m using rule, which I took over from more experienced developers than I am, that at the first place, when could be thrown some foreign exception, there it should be wrappped into custom, because you don´t want to be dependent on the other module´s exception.
Then If you will use method anywhere, you will put only MyGeneralException into method signature throws and it will bubble up through the layers of application. It should be catched and processed at the highest level, mostly exception message is used to creating response by some handler, or it could be handled manually.
Mainly during designing exception handling there should be considered, if your library will use third party developers, they are not interest in processing many exceptions.
This is what I usually say about exception handling:
So, I would say your sample code could be ok. It really depends on the "Custom error message" (and possibly exception custom properties - make sure they are serializable). It has to add value or meaning to help diagnose the problem. For exemple, this looks quite ok to me (could be improved):
string filePath = ... ;
try
{
CreateTheFile(filePath);
DoThisToTheFile(filePath);
DoThatToTheFile(filePath);
...
}
catch (Exception e)
{
throw new FileProcessException("I wasn't able to complete operation XYZ with the file at '" + filePath + "'.", e);
}
This doesn't:
string filePath = ... ;
try
{
CreateTheFile(filePath);
DoThisToTheFile(filePath);
DoThatToTheFile(filePath);
}
catch (Exception e)
{
throw new Exception("I wasn't able to do what I needed to do.", e);
}
The general principal with Exceptions is to catch them and handle them locally if applicable and otherwise allow them to bubble up to the top level container.
It all depends on the context that your application is running in. For example if the application is an ASP.Net web application then certain exceptions can be handled by the ASP application server in IIS but expected application specific errors (ones that are defined in your own interfaces) should be caught and presented to the end user if appropriate.
If you are dealing with I/O then there are a lot of factors that are beyond your control (network availability, disk hardware, etc.) so if there is a failure here then it is a good idea to deal with it straight away by catching the exception and presenting the user with and error message.
Most importantly don't fail silently so don't wrap the exceptions in your own exception and then ignore them. Better to allow them to bubble up and find them in the web server log for example.
First universal rule - Never swallow exceptions !! When you write a piece of code, you should be able to predict most of the cases where there could be an exception, and requires to be notified, such cases should be handled in the code. You should have a general logging module, that logs any exception caught by the application, but might not be of any use to the user actually.
Show the errors that are relevant to the user, for example the user of the system might not understand IndexOutOfRange exception, but it might be an interesting subject for us to research on why this occurred.
We need to always know what went wrong and when so that we could analyze the root cause and prevent it from happening again, because if it could happen once. It will happen again and may be it could cause a disaster. The only way to find what is wrong is by logging in what ever error the application encounters.
There's an excellent blog post by Eric Lippert, "Vexing exceptions". Eric answers your question with some universal guidelines. Here is a quote from the "sum up" part:
- Don’t catch fatal exceptions; nothing you can do about them anyway, and trying to generally makes it worse.
- Fix your code so that it never triggers a boneheaded exception – an "index out of range" exception should never happen in production code.
- Avoid vexing exceptions whenever possible by calling the “Try” versions of those vexing methods that throw in non-exceptional circumstances. If you cannot avoid calling a vexing method, catch its vexing exceptions.
- Always handle exceptions that indicate unexpected exogenous conditions; generally it is not worthwhile or practical to anticipate every possible failure. Just try the operation and be prepared to handle the exception.