extract an object from a TObjectList
-
08-07-2019 - |
Question
I have a TObjectList with OwnsObjects = true. It contains quite a few objects. Now I want to remove the object at index Idx from that list, without freeing it.
Is the Extract method the only option?
ExtractedObject := TheList.Extract(TheList[Idx]);
All other methods seem to free the object. I am looking for something a little bit more efficient, that does not do a linear search every time, since I already know the index of the object. Something like an overloaded ...
ExtractedObject := TheList.Extract(Idx);
... which does not exist.
Solution
Why not just set OwnsObjects to false, do your removal, then set it to true again?
OTHER TIPS
If you look at the code for delete, it's the notify method which causes the freeing to happen.
This should work :
TMyObjectList = Class(TObjectList)
private
fNotify: Boolean;
{ Private declarations }
procedure EnableNotification;
procedure DisableNotification;
protected
procedure Notify(Ptr: Pointer; Action: TListNotification); override;
public
constructor Create(AOwnsObjects: Boolean);overload;
constructor Create; overload;
function Extract(const idx : Integer) : TObject;
end;
constructor TMyObjectList.Create(AOwnsObjects: Boolean);
begin
inherited Create(AOwnsObjects);
fNotify := True;
end;
constructor TMyObjectList.Create;
begin
inherited Create;
fNotify := True;
end;
procedure TMyObjectList.DisableNotification;
begin
fnotify := False;
end;
procedure TMyObjectList.EnableNotification;
begin
fNotify := True;
end;
function TMyObjectList.Extract(const idx: Integer) : TObject;
begin
Result := Items[idx];
DisableNotification;
try
Delete(idx);
finally
EnableNotification;
end;
end;
procedure TMyObjectList.Notify(Ptr: Pointer; Action: TListNotification);
begin
if fNotify then
inherited;
end;
This is where class helpers can be usefull
TObjectListHelper = class helper for TObjectList
function ExtractByIndex(const AIndex: Integer): TObject;
end;
function TObjectListHelper.ExtractByIndex(const AIndex: Integer): TObject;
begin
Result := Items[AIndex];
if Result<>nil then
Extract(Result);
end;
You can now use:
MyObjList.ExtractByIndex(MyIndex);
The proposed helperclass (by Gamecat) will result in the same lookup that Thomas would like to get rid of.
If you take a look at the source, you can see what Extract() really does, and then use the same approach.
I will suggest something like tis:
obj := list[idx];
list.list^[idx] := nil; //<- changed from list[idx] := nil;
list.delete(idx);
This will give you the object, as Extract() does, and then delete it from the list, without any lookups. Now you can put this in a method some where, a helperclass or subclass or wher ever you like.
I don't use Delphi/C++Builder some time ago, but as far as I can renmember thats the only way. My suggestion is to use a TList instead, and manually delete the objects when required.
Anything wrong with:
ExtractedObject := TExtractedObject.Create;
ExtractedObject.Assign(Thelist[Idx]);
TheList.Delete(idx);
There is time needed for the create and assign but not for the search of the list. Efficiency depends on the size of the object -v- the size of the list.