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.
Try to study this article by Marco Cantù (the Delphi product manager):
ReplyDeletehttps://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.
Create inside the try and nil before is pointless because if the ctor raises the dtor is automatically called.
ReplyDeleteYour 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.
DeleteI don't see multiple objects in this article which I commented on, so what are we discussing?
DeleteAlso when you create nested try finally blocks to protect multiple layers of resources it might be better to refactor them into separate routines anyway.
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@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?
ReplyDeleteI've got overloaded versions of InitializeNil and FreeAndNil functions that take one or more object variables. The code then looks like this:
ReplyDeleteInitializeNil(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
@Anonymous this is interesting, can you please share the code for those overloaded versions of InitializeNil and FreeAndNil methods?
ReplyDeleteI always assign to nil first, and places the create inside a guarded section for complete error check of create statement.
ReplyDeleteFinally? 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.