22

Should catch blocks be used for writing logic i.e. handle flow control etc? Or just for throwing exceptions? Does it effect efficiency or maintainability of code?

What are the side effects (if there are any) of writing logic in catch block?

EDIT:

I have seen a Java SDK class in which they have written logic inside the catch block. For example (snippet taken from java.lang.Integer class):

        try {
            result = Integer.valueOf(nm.substring(index), radix);
            result = negative ? new Integer(-result.intValue()) : result;
        } catch (NumberFormatException e) {
            String constant = negative ? new String("-" + nm.substring(index))
                                       : nm.substring(index);
            result = Integer.valueOf(constant, radix);
        }

EDIT2:

I was going through a tutorial where they count it as an advantage of writing logic of exceptional cases inside the exceptions:

Exceptions enable you to write the main flow of your code and to deal with the exceptional cases elsewhere.

Any specific guidelines when to write logic in catch block and when not to?

gnat
  • 21,442
  • 29
  • 112
  • 288
HashimR
  • 323
  • 1
  • 2
  • 7
  • Exceptions should never be caught. If you need to catch an exception, it's an ineffective code sprinkled with magic gotos, and are handling non-exceptional circumstances. – Coder Apr 25 '12 at 12:40
  • 12
    @Coder I think you overgeneralize a bit. Especially since with many of the existing APIs you can't avoid it. – CodesInChaos Apr 25 '12 at 12:46
  • 8
    @Coder: In Java exceptions are often used as a mechanism for signalling issues that are part of the legitimate code flow. For instance, if you try to open a file and it fails, the Java library tells you that by throwing an exception, because the APIs that lead to opening a file have no other mechanism of returning an error. – JeremyP Apr 25 '12 at 12:49
  • That's an API/language issue. C# also had this int.parse format exception. That doesn't make it exceptional in any way. – Coder Apr 25 '12 at 12:57
  • 4
    @Coder Depends. I think when doing IO, or when parsing complex data, which is almost always correctly formatted, throwing an exception to signify an error makes the code much cleaner. – CodesInChaos Apr 25 '12 at 13:12
  • 1
    @Coded Yes, the execption is there for the method to tell you it was unable to do what was asked of it. But exceptions shouldn't be used for flow control, which is why C# also has an int.TryParse method that doesn't throw. – Andy Apr 25 '12 at 13:43
  • 1
    @Coder: That's utter rubbish. Things that are exceptional on one level of code may not be exceptional on another- or you may wish to, for example, present or add error or debug information before continuing. – DeadMG Apr 25 '12 at 14:36
  • 1
    Try/catch is often used in Java as an alternative to the C style err = myFunction(x); if(!err) {} – Michael Shopsin Apr 25 '12 at 14:45
  • 1
    Possibly related: http://programmers.stackexchange.com/q/97874/6384 http://stackoverflow.com/q/5378005/486504 – user Apr 26 '12 at 09:17
  • The most efficient usage of a try/catch block is not to use one. Pass an out parameter into the function and only set it to a successfully parsed value. Ie, that's how TryParse is implemented. – Evan Plaice Apr 27 '12 at 08:42

6 Answers6

46

The example you cite is due to poor API design (there is no clean way to check whether a String is a valid integer except trying to parse it and catching the exception).

At the technical level, throw and try/catch are control flow constructs that allow you to jump up the call stack, nothing more and nothing less. Jumping the up the call stack implicitly connects code that is not close together in the source, which is bad for maintainability. So it should only be used when you need to do that and the alternatives are even worse. The widely accepted case where the alternatives are worse is error handling (special return codes that need to be checked and passed up each level of the call stack manually).

If you have a case where the alternatives are worse (and you really have considered all of them carefully), then I'd say using throw and try/catch for control flow is fine. Dogma is not a good substitute for judgement.

gnat
  • 21,442
  • 29
  • 112
  • 288
Michael Borgwardt
  • 51,037
  • 13
  • 124
  • 176
  • 1
    +1, good points made. I think that some processing may be valid in the catch such as preparation of an error message and error logging. Freeing up of expensive resources such as db connections and in (.NET) COM references could be added to the Finally block. – NoChance Apr 25 '12 at 12:34
  • @EmmadKareem I thought about freeing resources, but usually you have to free those anyway at some point even without error situation. Thus you might repeat yourself. Preparing a log message is of course acceptable. – scarfridge Apr 25 '12 at 13:28
  • @MichaelBorgwardt I think the API design is not so poor (though it could be better). Trying to parse an invalid string is clearly an error condition and thus exactly the "widely accepted case" you mentioned. Agreed, a method to determine if a string is syntactically correct number comes in handy (this is where it could be better). However, forcing everybody to call this method before actual parsing is not possible and we need to handle the error. Mixing actual result and error code is uncomfortable and so you'd still throw the exception. But maybe a runtime exception. – scarfridge Apr 25 '12 at 13:50
  • 3
    +1 for that last sentence. – FrustratedWithFormsDesigner Apr 25 '12 at 13:58
  • 2
    @scarfridge: I agree completely with you :) The absence of a boolean-returning isInteger(String) method is all I meant with "poor API design" – Michael Borgwardt Apr 25 '12 at 14:21
  • -1: isInteger doesn't help. Instead of `try { parse(s); ...; } catch { oops; }` you have `if (isInteger(s)) { parse(s); ...; } else { oops; }`. Now the parsing happens twice. Worst of all would be for parse to return a special value instead of throwing. – kevin cline Apr 25 '12 at 19:16
  • 'throw' does more than *jump* up the call stack. It unwinds the call stack. The code isn't "connected together" any more than the code inside a function is connected to the caller. – kevin cline Apr 25 '12 at 19:22
  • @kevin cline: wrong - isInteger() would not be used at all in cases where a malformed one is a real error condition (parse would still throw an exception), but it would allow a much cleaner handling of use cases like the one quoted by the OP, where it's just a different branch in the logic. – Michael Borgwardt Apr 26 '12 at 05:48
  • @kevin cline: yes, but that *is* a connection, and with the function call, it's explicit while a catch is rather implicit and easy to miss or mess up. – Michael Borgwardt Apr 26 '12 at 05:51
9

This is something that tends to be dependent on language and paradigm.

Most of my work is done in Java (and sometimes C++). The tendency is to use exceptions for exceptional conditions only. There are some questions on Stack Overflow about the performance overhead of exceptions in Java, and as you can see, it's not exactly negligible. There are also other concerns, such as the readability and maintainability of the code. When used properly, exceptions can delegate error handling to the appropriate components.

However, in Python, the idea that it is easier to ask forgiveness than permission reigns. In the Python community, this is known as EAFP, which is contrasted with the "look before you leap" (LBYL) approach in C-style languages.

Thomas Owens
  • 79,623
  • 18
  • 192
  • 283
  • 2
    +1 for mentioning the overhead with exceptions. I do .NET and (at least on my machine) I could even feel when an exception is about to happen by the duration it takes in some cases compared to other normal operations. – NoChance Apr 25 '12 at 12:36
  • 2
    @EmmadKareem Only if you have a debugger attached. You can throw several thousand of them per second without a debugger. – CodesInChaos Apr 25 '12 at 12:49
  • @CodeInChaos, you are correct. How would I know, I write such a clean code that never get any at run time :) – NoChance Apr 25 '12 at 13:04
  • @EmmadKareem Depending on how one writes a network application, connection or protocol failures are handled with an exception. Exceptions need to be reasonably fast in such an application to avoid trivial DoS attacks. – CodesInChaos Apr 25 '12 at 13:09
  • @CodeInChaos, this is good to know. I was kidding in my last comment. – NoChance Apr 25 '12 at 13:11
  • @CodeInChaos, FYI, I wanted to know more about the effect on performance and I found this link (please see user's Tobi's answer): http://stackoverflow.com/questions/52312/what-is-the-real-overhead-of-try-catch-in-c – NoChance Apr 25 '12 at 15:17
  • +1 for mentioning programming-language specific cultures – tdammers Apr 25 '12 at 19:08
  • +1 for mentioning the Easier to Ask for Forgiveness than Permission approach :) – epidemian Apr 28 '12 at 00:07
9

That is not the best way of thinking about try/catch blocks. The way to think about try/catch blocks is this: a failure has happened, where is the best place to deal with THIS specific failure. That may be the very next line of code, it may be twenty levels up the call chain. Wherever it is, that is where it should be.

What the catch block does depends upon the error and what you can do about it. Sometimes it can simply be ignored (failure to delete a scratch file with no imortant data in it), sometimes it will be used to set a functions return value to true or false (java's int parse method), sometimes you'll exit the program. All of that depends upon the error.

The important part to understand is: catch block == I know how to deal with this.

jmoreno
  • 10,640
  • 1
  • 31
  • 48
6

Catch blocks should only be used for handling exceptions, nothing else and never ever for control flow. In any situation where you want to use exceptions to manage flow you would be better off checking pre-conditions.

Handling flow with exceptions is slow and semantically incorrect.

Tom Squires
  • 17,695
  • 11
  • 67
  • 88
  • 4
    I know what you are saying, but it is worth noting that all that exceptions does is handle program flow - just that it should only be used to control exceptional error flow... – Max Apr 25 '12 at 12:10
  • Ain't there any specific guidelines when to write logic in catch block and when not to? – HashimR Apr 25 '12 at 12:24
  • 4
    Java's Number parsers and Text formatters are famous examples of problematic exception usage: the only reasonable way to check for a precondition (that a string represents a parseable number) is to try parsing it, and if it fails with an exception, what are your choices in practice? You have to catch the exception and manage flow with it, no matter that it's considered semantically incorrect. – Joonas Pulakka Apr 25 '12 at 12:35
  • @JoonasPulakka I think your comment wrt _Number parsers and Text formatters_ qualifies as a full-size answer to this question – gnat Apr 25 '12 at 17:32
5

Should catch blocks be used for writing logic i.e. handle flow control etc? Or just for throwing exceptions? Does it effect efficiency or maintainability of code?

First, forget the notion that "exceptions should be used for exceptional conditions". Also stop worrying about efficiency, until you have code that performs unacceptably and you have measured to know where the problem lies.

Code is easiest to understand when actions follow in a simple sequence, without conditionals. Exceptions improve maintainability by removing error checking from the normal flow. It doesn't matter if execution follows the normal flow 99.9% of the time or 50% of the time or 20% of the time.

Throw an exception when a function is unable to return a value, when a procedure is unable to complete the expected action, or when a constructor is unable to produce a usable object. This allows programmers to assume that a function always returns a usable result, a procedure always completes the requested action, a constructed object is always in a usable state.

The biggest problem I see with exception handling code is that programmers write try/catch blocks when they should not. For example, in most web applications, if a database request throws an exception, there is nothing that can be done in the controller. One generic catch clause at the highest level is enough. Then the controller code can blissfully ignore the possibility that the disk has crashed or the database is offline or whatever.

kevin cline
  • 33,608
  • 3
  • 71
  • 142
1

Catch blocks should not be used for writing code logic. They should be used only for handling errors. An example is (1) cleanup any allocated resources, (2) print a useful message, and (3) exit gracefully.

It's true that exceptions can be abused and cause undesired goto effects. But that doesn't make them useless. When properly used, they can improve many aspects of your code.

sakisk
  • 3,377
  • 2
  • 24
  • 24