I am currently doing a code review and the following code made me jump. I see multiple issues with this code. Do you agree with me? If so, how do I explain to my colleague that this is wrong (stubborn type...)?
- Catch a generic exception (Exception ex)
- The use of "if (ex is something)" instead of having another catch block
- We eat SoapException, HttpException and WebException. But if the Web Service failed, there not much to do.
Code:
try
{
// Call to a WebService
}
catch (Exception ex)
{
if (ex is SoapException || ex is HttpException || ex is WebException)
{
// Log Error and eat it.
}
else
{
throw;
}
}
The mantra is:
- You should only catch exceptions if
you can properly handle them
Thus:
- You should not catch general
exceptions.
In your case, yes, you should just catch those exceptions and do something helpful (probably not just eat them--you could throw
after you log them).
Your coder is using throw
(not throw ex
) which is good.
This is how you can catch multiple, specific exceptions:
try
{
// Call to a WebService
}
catch (SoapException ex)
{
// Log Error and eat it
}
catch (HttpException ex)
{
// Log Error and eat it
}
catch (WebException ex)
{
// Log Error and eat it
}
This is pretty much equivalent to what your code does. Your dev probably did it that way to avoid duplicating the "log error and eat it" blocks.
I am currently doing a code review and the following code made me jump. I see multiple issues with this code. Do you agree with me?
Not totally, see below.
- Catch a generic exception (Exception ex)
In general, catching a generic exception is actually ok as long as you rethrow it (with throw;) when you come to the conclusion that you can't handle it. The code does that, so no immediate problem here.
- The use of "if (ex is something)" instead of having another catch block
The net effect of the catch block is that only SoapException, HttpException, etc. are actually handled and all other exceptions are propagated up the call stack. I guess functionality-wise this is what the code is supposed to do, so there's no problem here either.
However, from a aesthetics & readability POV I would prefer multiple catch blocks to the "if (ex is SoapException || ..)". Once you refactor the common handling code into a method, the multiple catch blocks are only slightly more typing and are easier to read for most developers. Also, the final throw is easily overlooked.
- We eat SoapException, HttpException and WebException. But if the Web Service failed, there not much to do.
Here possibly lurks the biggest problem of the code, but it's hard to give advice without knowing more about the nature of the application. If the web service call is doing something that you depend on later then it's probably wrong to just log & eat the exceptions. Typically, you let the exception propagate to the caller (usually after wrapping it into e.g. a XyzWebServiceDownException), maybe even after retrying the webservice call a few times (to be more robust when there are spurious network issues).
The problem with catching and re-throwing the same exception is that, although .NET does its best to keep the stack trace intact, it always ends up getting modified which can make tracking down where the exception actually came from more difficult (e.g. the exception line number will likely appear to be the line of the re-throw statement rather than the line where the exception was originally raised). There's a whole load more information about the difference between catch/rethrow, filtering, and not catching here.
When there is duplicate logic like this, what you really need is an exception filter so you only catch the exception types you're interested in. VB.NET has this functionality, but unfortunately C# doesn't. A hypothetical syntax might look like:
try
{
// Call to a WebService
}
catch (Exception ex) if (ex is SoapException || ex is HttpException || /* etc. */)
{
// Log Error and eat it
}
As you can't do this though, what I tend to do instead is use a lambda expression for the common code (you could use a delegate
in C# 2.0), e.g.
Action<Exception> logAndEat = ex =>
{
// Log Error and eat it
};
try
{
// Call to a WebService
}
catch (SoapException ex)
{
logAndEat(ex);
}
catch (HttpException ex)
{
logAndEat(ex);
}
catch (WebException ex)
{
logAndEat(ex);
}
Sometimes that is to only way to handle "catch every exception" scenarios, without actually catching every exception.
I think sometimes, say, lowlevel framework / runtime code needs to make sure that no exception is ever escaping. Unfortunately, there is also no way the framework code can know which exceptions are raised by the code executed by the thread.
In that case a possible catch block could look like this:
try
{
// User code called here
}
catch (Exception ex)
{
if (ExceptionIsFatal(ex))
throw;
Log(ex);
}
There are three important points here, however:
- This isn't something for every situation. In code reviews we are very picky about places where this is actually neccessary and thus allowed.
- The ExceptionIsFatal() method assures that we don't eat exceptions which should never be swallowed (ExecutionEngineException, OutOfMemoryException, ThreadAbortException, etc.)
- What is swallowed is carefully logged (event-log, log4net, YMMV)
Typically, I'm all for the practice of letting uncaught exceptions simply "crash" the application by terminating the CLR. However, especially in server applications, this is sometimes not sensible. If one thread encounters a problem which is deemed non-fatal, there is no reason in ripping the whole process down, killing off all other running requests (WCF, for example, handles some cases this way).
I would like to add here, because the Exception handling in almost all java / C# code that I have seen is just incorrect. I.e. you end up with very difficult to debug error for ignored Exceptions, or, equally bad, you get an obscure exception which tells you nothing, because blindly following the "catching(Exception) is bad" and things are just worse.
First, understand that an exception is a way to facilitate the returning of error information across code layers. Now, mistake 1: a layer is not just a stack frame, a layer is code which has a well defined responsibility. If you just coded interfaces and impls just because, well you have better things to fix.
If the layers are well designed and have specific responsibilities, then the information of the error has different meaning as it bubbles up. <-this is the key on what to do, there is no universal rule.
So, this means that when an Exception occurs you have 2 options, but you need to understand where in the layer you are:
A) If you are in the middle of a layer, and you are just an internal, normally private, helper function and something goes bad: DONT WORRY, let the caller receive the exception. Its perfectly OK because you have no business context and
1) You are not ignoring the error and
2) The caller is part of your layer and should have known this can happen, but you might not now the context to handle it down below.
or ...
B) You are the top boundary of the layer, the facade to the internals. Then if you get an exception the default shall be to CATCH ALL and stop any specific exceptions from crossing to the upper layer which will make no sense to the caller, or even worse, you might change and the caller will have a dependency to an implementation detail and both will break.
The strength of an application is the decoupling level between the layers. Here you will stop everything as a general rule and rethrow the error with a generic exception translating the information to a more meaningful error for the upper layer.
RULE: All entry points to a layer shall be protected with CATCH ALL and all errors translated or handled. Now this 'handeled' happens only 1% of the time, mostly you just need (or can) return the error in a correct abstraction.
No I am sure this is very difficult to understand. real Example ->
I have a package that runs some simulations. These simulations are in text scripts. there is a package that compiles these scripts and there is a generic utils package that just reads text files and, of course, the base java RTL. The UML dependency is->
Simulator->Compiler->utilsTextLoader->Java File
1) If something breaks in the utils loader inside one private and I get a FileNotFound, Permissions or what ever, well just let it pass. There is nothing else you can do.
2) At the boundary, in the utilsTextLoader function initially called you will follow the above rule and CATCH_ALL. The compiler does not care on what happen, it just needs to now whether the file was loaded or not. So in the catch, re throw a new exception and translate the FileNotFound or whatever to "Could not read file XXXX".
3) The compiler will now know that the source was not loaded. Thats ALL it needs to know. So if I later I change utilsTestLoader to load from network the compiler will not change. If you let go FileNotFound and later change you will impact compiler for nothing.
4) The cycle repeats: The actual function that called the lower layer for the file will do nothing upon getting the exception. So it lets it go up.
5) As the exception gets to the layer between simulator and compiler the compiler again CATCHES_ALL, hiding any detail and just throws a more specific error: "Could not compile script XXX"
6) Finally repeat the cycle one more time, the simulator function that called the compiler just lets go.
7) The finally boundary is to the user. The user is a LAYER and all applies. The main has a try that catches_ALL and finally just makes a nice dialog box or page and "throws" a translated error to the user.
So the user sees.
Simulator: Fatal error could not start simulator
-Compiler: Could not compile script FOO1
--TextLoader: Could not read file foo1.scp
---trl: FileNotFound
Compare to:
a) Compiler: NullPointer Exception <-common case and a lost night debugging a file name typo
b) Loader: File not found <- Did I mention that loader loads hundreds of scripts ??
or
c) Nothing happens because all was ignored!!!
Of course this assumes that on every rethrow you didn't forget to set the cause exception.
Well my 2cts. This simple rules have saved my life many times...
-Ale
You should usually still catch generic exceptions in a global handler (which is the perfect place to log unexpected Exceptions), but otherwise as said before, you should only catch specific exception types in other places if you plan to do something with them. The catch blocks should look for those exception types explicitly, not as the code you posted does.
I don't think this is such a bad thing in this case, but I also do something similar in my code with exceptions that can be safely ignored being caught and the rest being re-thrown. As noted by Michael's response, having each catch being a separate block could cause some readability issues which are prevented by going this route.
In regards to pointing this out to your colleague, I think you would have a hard time convincing them that this is the wrong way of doing things - even more so if they are stubborn - because of the potential readability issues with doing things the other way. Since this version is still throwing the generic exception's that can't be handled it is in keeping with the spirit of the practice. However, if the code was in line with the following:
try
{
// Do some work
}
catch (Exception ex)
{
if (ex is SoapException)
{
// SoapException specific recovery actions
}
else if (ex is HttpException)
{
// HttpException specific recovery actions
}
else if (ex is WebException)
{
// WebException specific recoery actions
}
else
{
throw;
}
}
Then I think you would have a bit more of a reason to be concerned as there is no point in doing work for a specific exception by checking for it in a general exception block.
the princeple is only to catch the exception you can handle.
such as, if you know how to deal with findnotfound, you catch the filenotfoundexception, otherwiese, do NOT catch it and let it be thrown to the upper layer.