Friday, 24 November 2023

How to write try finally blocks

 I recently came across code another developer had done and they write try..finally blocks like the following:

procedure TSomeClass.DoSomething: string;
var
    myClass: TMyClass;
begin
    myClass := nil;
    try
        myClass := TMyClass.Create;
        // Do stuff
    finally
        myClass.Free;
    end;
end;

Were I would write it as follows:

procedure TSomeClass.DoSomething: string;
var
    myClass: TMyClass;
begin
    myClass := TMyClass.Create;
    try
        // Do stuff
    finally
        myClass.Free;
    end;
end;

I always create the object on the line before the try and would not set the object to nil just before creating it. I believe setting the object to nil before creating it and putting the create after the try to not be the correct way and do not know the reasoning why the developer does it like this.


9 comments:

  1. Try to study this article by Marco Cantù (the Delphi product manager):
    https://blog.marcocantu.com/blog/2018-january-multiple-try-finally-delphi.html
    And you will understand *why* setting the object to nil before creating it and putting the create after the try is *absolutely fine*. It is even a recommended best practice by many Deplhi gurus.

    ReplyDelete
  2. Create inside the try and nil before is pointless because if the ctor raises the dtor is automatically called.

    ReplyDelete
    Replies
    1. Your advice is only valid in this one case. What if you are creating multiple objects? Reading your advice one gets impression that all destructors will be automatically called when there is an exception in one constructor which is not the case. So you have left a very dangerous comment for some less experienced developer to find and misunderstand.

      Delete
    2. I don't see multiple objects in this article which I commented on, so what are we discussing?
      Also when you create nested try finally blocks to protect multiple layers of resources it might be better to refactor them into separate routines anyway.

      Delete
  3. Perhaps the former style becomes more helpful if you have more than one variable to create/free. To avoid having multiple nested try..finally blocks you can assign them all nil and have just one try..finally block to handle them all.

    ReplyDelete
  4. @StefanGlienke you are absolutely right about the possible dangers of exception handling involving ctor dtor. What is your opinion about creating interfaced "Guards" like the those provided by JCL?

    ReplyDelete
  5. I've got overloaded versions of InitializeNil and FreeAndNil functions that take one or more object variables. The code then looks like this:

    InitializeNil(Var1, Var2, Var3);
    try
    Var1 := TObject1.Create(...);
    // ...
    Var2 := TObject2.Create(...);
    // ...
    Var3 := TObject3.Create(...);
    // ...
    finally
    FreeAndNil(Var1, Var2, Var3); // or possibly FreeAndNil(Var3, Var2, Var1);
    end;

    I think I got this idea from a post by David Heffernan in Delphi Praxis

    ReplyDelete
  6. @Anonymous this is interesting, can you please share the code for those overloaded versions of InitializeNil and FreeAndNil methods?

    ReplyDelete
  7. I always assign to nil first, and places the create inside a guarded section for complete error check of create statement.

    Finally? What is the point of that? There is NO error protection for the actions inside finally section, it is the same as write the free statement after the try-finally-end statement.

    ReplyDelete