The finally
block for a try-catch
structure is well known, and gives a really easy and elegant way to deal with some must-be-done code.
Therefore, I can see no reason why It shouldn’t be good for methods too. For instance, lets say I’m writing some very complicated logic in a method, and I expect to end up with a bunch of boolean flags that will in turn lead to some decisions. Many times in such kind of methods I have branches of code where I would want to “break” the flow and just perform the “real stuff” that I called this method for, with the satisfying information I gathered so far.
So, Why isn’t there such pattern? Or is there?
7
The first thing is:
Don’t have too complex functions doing too complicated things.
If you can’t avoid complex functions, there are various other tools to handle the situation.
In C and other languages people often use goto
for the reason.
void func() {
/* .. code .. */
if (condition()) goto end;
/* ... more code ...*/
end:
cleanup();
}
Now some people dislike goto
even in such cases, so they emulate goto
using a do { ... } while(0)
loop:
void func() {
do {
/* .. code .. */
if (condition()) break;
/* ... more code ...*/
} while(0);
cleanup();
}
Some languages, for instance PHP, allow giving a parameter to the break
-keyword to jump multiple levels out:
function func() {
do {
/* .. code .. */
if(something() {
/* some code */
if (condition()) break 2;
/* ... */
}
/* ... more code ...*/
} while(0);
cleanup();
}
But there are languages with higher-level constructs. C++ for instance has destructors, there people use the RAII pattern:
struct cleanup {
~cleanup() {
/* .. do cleanup ... */
}
}
void func() {
cleanup cleaner; // this stack object will be destroyed on
// scope exit by calling the destructor
if (condition()) return;
/* ... more code ...*/
}
Some other languages have neither goto
, nor nested break, nor destructors. Thus Java programmers have to live with nested if’s, abuse exceptions, … or make the code simpler.
public static void func() {
try {
/* .. code .. */
if(something() {
/* some code */
if (condition()) throw new ControlFlowException();
/* ... */
}
/* ... more code ...*/
} catch (ControlFlowException e) {
/* ignore */
} finally {
cleanup();
}
}
So yes, alternative concepts exist and differ by language.
9
My guess is that there isn’t a huge need for that sort of feature for two reasons:
- Most languages already give you pretty explicit flow control with looping and branching constructs.
- One of the most common uses of
finally
is to ensure resources are safely disposed. If your method is so complicated that you need this, you should probably refactor it into more manageable pieces.
It’s worth noting that C# has the using
statement which will automatically call Dispose()
on the object when it leaves scope. This includes disposing of it after a return
statement, so it acts like a finally
in that respect.
Note that this is tied to the object, not tied to the function. But it does handle the main usecase for a finally
in a function.
3
The Go programming language has something similar to this idea: the defer statement. Deferred functions are called when the enclosing function exits for any reason, and can be method calls or plain functions, e.g.:
func fileProcessor(filename string) (err error) {
var file *os.File
if file, err = os.Open(filename); err != nil {
return
}
defer func() {
fmt.Printf("Closing filen")
file.Close()
}()
fmt.Printf("Processing filen")
return
}
In this case, the deferred function is anonymous. It could also just be:
defer file.Close()
But the anonymous routine allowed the Printfs in the example to highlight the order of execution.
In practice, I find defers to be a clean way to ensure the release of locally acquired resources. The approach isn’t commonly found in other languages, but I find its presence in Go to be a welcome one, and would like to see it elsewhere.
1
Extract method sounds like a good plan: break up you method into an “initialization” function, a “do your thing” function and a “finalization” function. That way the finalization will always be done regardless of what happens in “do your thing”. Except in the case of exceptions of course, but that is where the finally
block of your languages comes in. You would then put the “finalization” function in the finally block.
procedure MainFunction;
begin
InitializationFunction;
try
DoYourThingFunction;
finally
FinalizationFunction;
end;
end;
0
finally
causes a lot of code repetition. If you use a resource Foo
in 100 places, and the cleanup of Foo
takes 3 lines, then the finally
blocks would add 300 lines of code but a destructor adds only 3 lines total.
5
Does this really make your code more readable than alternatives? You could already put a try/finally around the code inside your function or you could put the “Finally” code right after the method is called.
Although it’s a reasonable concept, every new feature you place into a language requires many many programmer hours of time to learn and understand–hundreds–thousands… If there is any way you can use an existing construct instead without causing a lot of duplication, boilerplate or confusion then it would be much better to use the existing mechanism.
3
Some years later, several languages have a “defer” statement. Say you open a file, and you know you’ll need to close it eventually. So you write
Open file f
Defer close file f
The compiler guarantees that the defer statement will be called as late as possible. The good thing is that you can write the cleanup together with resource creation, at the point where the knowledge is there.
Actually, the TTCN-3 language was extended recently to support using finally and catch on function level.
Link to the standard
For example:
testcase t_myTest1() runs on myComponent {
f_init("user1");
f_init("unknown user");// bad argument will raise an exception in f_init
... // because of the raised exception execution continues in the finally block
} finally {
... // freeing up resources
}
Testcases have to be independent, without any side effects on each other.
At the same time, by their very purpose, they might uncover situations in the system under test, that might break their usual execution paths … but the resources allocated for the test would still need to be freed.
As such it makes sense for the language to offer a syntax that lets test developers achieve their aim (of freeing the resources), without having to put the logic of each testcase/function/etc… in a try-catch-finally block, which would just push the actual main logic of the test/function +1 level deeper.