Delphi. Automatic destruction of objects upon exiting the method
I think all Delphians wrote a similar code thousands of times:
var
MyObj: TMyObj;
begin
MyObj := TMyObj.Create;
try
MyObj.DoWork; // работаем с MyObj
finally
MyObj.Free;
end;
end;
Let's just say: such a scribble bothers. I want the objects themselves to be destroyed when they exit the function / procedure.
Well, this is easy enough to implement. As a result, we get something like this code:
var
MyObj: TMyObj;
begin
MyOb := CreateObjectDestroyer(TMyOb.Create).ObjectAsPtr;
MyObj.DoWork; // работаем с MyObj
end; // тут объект MyObj уничтожится
Below I will describe how to implement this behavior.
I’ll point out right away that all the code below will work in all versions of the dolphin that have interfaces (in my opinion, starting from the third).
As is known for interfaces, Delphi maintains an automatic reference count:
var
Intf: IUnknown;
begin
Intf := TInterfacedObject.Create; // тут неявно вызывается IUnknown.AddRef
// что то делаем
end; // тут неявно вызывается IUnknown.Release и объект разрушается
Now let's write the TInterfacedObject descendant, which will destroy the object passed to it in its destructor. Well, plus our object will implement a small additional interface.
type
IObjectDestroyer = interface(IInterface)
['{4DE81104-08B2-4821-960E-8935AC9B8F5E}']
function GetObjectAsPtr: Pointer;
property ObjectAsPtr: Pointer read GetObjectAsPtr; // это ссылка на тот объект, который мы хотим уничтожить
end;
type
TObjectDestroyer = class(TInterfacedObject, IObjectDestroyer)
strict private
FObject: TObject;
protected
function GetObjectAsPtr: Pointer;
public
constructor Create(AObject: TObject);
destructor Destroy; override;
end;
constructor TObjectDestroyer.Create(AObject: TObject);
begin
inherited Create();
FObject:=AObject;
end;
destructor TObjectDestroyer.Destroy;
begin
FObject.Free;
inherited Destroy;
end;
function TObjectDestroyer.GetObjectAsPtr: Pointer;
begin
Result := FObject;
end;
Now you can work with the object like this:
var
Destroyer: IObjectDestroyer ;
begin
Destroyer := TObjectDestroyer.Create(TMyObj.Create);
TMyObj(Destroyer.ObjectAsPtr).DoWork();
end;
We got rid of the need to explicitly destroy MyObj, but the need to write TMyObj (Destroyer.ObjectAsPtr) is depressing to say the least. Well, let's describe an additional function:
function CreateObjectDestroyer(AObject: TObject): IObjectDestroyer;
begin
Result:=TObjectDestroyer.Create(AObject);
end;
and remove from our code, in general, the Destroyer variable we do not need:
var
MyObj: TMyObj;
begin
MyOb := CreateObjectDestroyer(TMyOb.Create).ObjectAsPtr;
MyObj.DoWork;
end;
Call CreateObjectDestroyer implicitly create a variable (what we described in the previous version as Destroyer).
Well, actually the problem is solved, but I would like to mention some unobvious moments and frank shortcomings of this approach:
So, the first is typing. More precisely, its absence. the ObjectAsPtr property is specifically declared as Pointer so that it can be assigned to a variable of any class:
var
Obj1: TMyObj1;
Obj2: TMyObj2;
begin
Obj1 := CreateObjectDestroyer(TMyObj1.Create).ObjectAsPtr;
Obj2 := CreateObjectDestroyer(TMyObj2.Create).ObjectAsPtr;
end;
but in this case, the compiler does not monitor the types and we can mess up:
Obj1 := CreateObjectDestroyer(TMyObj2.Create).ObjectAsPtr;
most likely in this case we are waiting for Access violation.
You can add the AObject: TObject property to the IObjectDestroyer interface and cast types explicitly
Obj1 := CreateObjectDestroyer(TMyObj2.Create).AObject as TMyObj1;
here the error will be more obvious, but still it is a run-time check. In the comp time for delphos younger than the 2009 version, this problem is not solved, generics appeared in older versions, you can don’t sacrifice typing there.
The second problem: the implicit moment of destruction of the object.
In theory, the compiler can call Release for immediately after calling CreateObjectDestroyer
Obj: = CreateObjectDestroyer (TMyObj.Create) .ObjectAsPtr;
// The Obj link is invalid because the zarelizin interface and the MyObj object are destroyed.
However, Barry Kelly says that this behavior will not change in the future, you can read the discussion here.
Again, like the issue of typing in the 2009/2010 dolph, this drawback can be partially solved.
Well, in general, that's all, if you like this post, I will describe how to more transparently implement a similar mechanism on older versions of the dolphin.
PS Advise an analog of source code highlighter, which would support the Delphic syntax?