5

I know it sounds like an old stupid question but I searched all over the internet and I still don't understand one thing. I understand that try-finally will run the finally code before stopping for error (or don't stopping when no exceptioned was raised) and that try-except will run the except code when exception is raised. But what I still don't understand is the point of try-except within try-finally statements. I'll write an example

What I always do is something like this:

a:=x.Create;
try 
  a.DoRiskyStuff;
except
  ShowMessage('Error!');
end;
a.free;

I have never in a years used a finally clause because I always handle the errors and I never understood what is the point to use them both nested together. What's bad about my solution? Why in examples it's common to use something like this:

a:=x.Create;
try
  try
    a.DoRiskyStuff;
  except
    ShowMessage('Error!');
  end;
finally
  a.free;
end;

What's the difference? Why should somebody use a finally clause when all cleanup can be done after the try-except?

Thank you very much for any asnwers!

Edit: I changed the create lines so some of you want bash me for it ;)

jano152
  • 147
  • 1
  • 7
  • 3
    `they use` - who?? There is no `try-except-finally` construction in Delphi – MBo Jun 19 '16 at 11:30
  • 1
    Please, develop a minimal understanding, your snippet you're asking about is bogus and won't compile. Did you mean nesting `try...except` within `try...finally`? – Free Consulting Jun 19 '16 at 11:39
  • Both code excerpts are wrong, the second one does not even compile. – David Heffernan Jun 19 '16 at 11:58
  • I'm sorry I meant try-except within try-finally. I'll edit it. – jano152 Jun 19 '16 at 12:03
  • 1
    The first one is wrong because it calls the constructor on an instance. Your real problem is that you try to handle exceptions early. Don't. Let exceptions go. Let them be handled by something that knows how to handle them. – David Heffernan Jun 19 '16 at 12:50
  • You mean that Create should always be within the try-except? I use try-except only for code that has some risk in it. Like when there is input from user or writing to the file. Everything else is pretty much predictable. – jano152 Jun 19 '16 at 13:21
  • Oh, I see what you mean't. That was a simplified example. Of course that you can't call a constructor like that but that wasn't the point of my question... – jano152 Jun 19 '16 at 13:49
  • 2
    A finally block isn't necessarily for exceptions. They're also triggered by leaving a block via exit, break, or continue (but not by goto; that won't compile). – Rob Kennedy Jun 19 '16 at 15:11
  • 1
    The fact that your example code for the constructor wrong makes us wonder what else was wrong. Details matter. – David Heffernan Jun 19 '16 at 16:28
  • 2
    No, what I meant when discussion exceptions is that you should remove the exception handling and let it be handled by the top level exception handler. Then you should use finally to manage lifetime. – David Heffernan Jun 19 '16 at 16:28
  • FWIW, your first snippet should 1. not handle the exception, as @David wrote, and 2. it should contain a `finally`: `a := TA.Create; try ... finally a.Free end;`. That would make sense. – Rudy Velthuis Jun 19 '16 at 22:21
  • It's better to not handle the exception? What if a.DoRiskyStuff is for example a writing to a file and I want to catch some write errors like access denied? With finally wouldn't it show an ugly error message to the user without any helpful information for him to make the necessary changes (change the save location for example)? – jano152 Jun 20 '16 at 07:41
  • 1
    I deal with that by throwing an exception class that is treated differently by the top level exception handler. For instance for exceptions that are not down to programming errors, you want to report the error in a less scary way than, for instance, an access violation. But still you want to let the exception float upwards to the top. Otherwise, what happens when your program continues execution having failed to perform the first step of its task? Your goal should be not to handle exceptions. – David Heffernan Jun 20 '16 at 15:12
  • 1
    Well, thank you for the answer. I totally agree on this. Use try-finally in the lower levels and handle the errors in a user-friendly way on the top level. – jano152 Jun 20 '16 at 15:21
  • 1
    Try finally at all levels. Everywhere you acquire a resource that needs to be released you need to protect it with finally. Handling exceptions is a completely unrelated concern. – David Heffernan Jun 20 '16 at 17:15

1 Answers1

5

There are two big differences between those two constructs. A technical one and a semantical one.

Technical

Technically, the difference is that the finally block will always be executed, even if there is no exception, while the except block is only executed when there is an exception.

The finally block is even executed when you exit the function early using Exit in a try-finally block.

Additionally, the finally block does not swallow the exception, while the except block generally swallows it, and it can only escape the block if the type of exception does not match the exception types you specify in on ... do, or if you manually re-raise it.

Semantical

The except block is meant to handle the exception(s), while the finally block is not. The finally block is meant to contain code that should be executed regardless of an exception, i.e. mainly to protect resources. That is why you should do:

X := TY.Create;
try
  // Code that may raise an exception.
finally
  X.Free; // Free resource, even if there was an exception.
          // Exception is NOT handled.
end;

and:

try
  // Code that may raise an exception.
except
  // Handle the kind of exceptions you can handle.
end;

Note that resource protection with finally is not limited to memory and Free. You can restore/undo/close anything that should be restored/undone/closed, i.e. close opened files, close open connections, shut down hardware that was started, restore mouse pointers to their previous form, etc.etc.

So you can also use it for code like:

Cursor := crMultiDrag;
try
  // Code that may raise exception.
finally
  Cursor := crDefault;
end;
Community
  • 1
  • 1
Rudy Velthuis
  • 28,387
  • 5
  • 46
  • 94
  • 1
    Thank you for your answer. I understand these differences. What I don't understand is why they are used together like try-except within try-finally. Sorry, I wrote it wrong previously in my question but I corrected it already. – jano152 Jun 19 '16 at 12:08
  • They are seldom used together. The code you posted does not compile, BTW. You can nest them, but that is almost never necessary. They serve different purposes. – Rudy Velthuis Jun 19 '16 at 12:10
  • I just tried it and it compiled alright. And that's what I thought - that it is useless to use them together but when I was searching for usage of try-finally I've found people using it like that. – jano152 Jun 19 '16 at 12:15
  • You have no understanding of this at all. You still think that finally and except do the same thing. They don't. They are orthogonal. – David Heffernan Jun 19 '16 at 16:30
  • 1
    No, I didn't think it when I posted my question and I don't think it now. – jano152 Jun 19 '16 at 16:36
  • `try ... except ... finally ...end;` as you write it does not compile in Delphi. Only `try try ... except ... end finally ... end;` does. So I wonder how you compiled your code. And you really don't understand `finally`. If you did, you would use `finally` very often and `except` far less often. – Rudy Velthuis Jun 19 '16 at 22:15
  • 1
    You wrote: *I use try-except only for code that has some risk in it. Like when there is input from user or writing to the file. Everything else is pretty much predictable.* Which indicates that you are using try/except to protect resources rather than try/finally. – David Heffernan Jun 20 '16 at 05:54