views:

910

answers:

6

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.

A: 

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.

xoreax
+7  A: 

Why not just set OwnsObjects to false, do your removal, then set it to true again?

skamradt
too easy ;-) Thanks
Thomas Mueller
A: 

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;
Steve
+1  A: 

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);
Gamecat
Unfortunately this will still do a linear search in the original extract function, even though I already passed the object's index to the new extract function.But I guess combining this with OwnsObjects=False/True should do the trick.
Thomas Mueller
+1  A: 

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.

Vegar
Unfortunately this doesn't work either since Assigning NIL to an item automatically frees it.
Thomas Mueller
Oops. I completly overlooked that one, but TList gives you direct access to the linkedlist, through the public property List: PPoinerList. Change list[idx] := nil to list.List^[i] := nil; and the solution should be sound again.
Vegar
A: 

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.

Despatcher
that approach requires the object to descend from TPersistent and to properly implement the Assign method
Oliver Giesen