Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Which lists could serve as temporary lists?

When working with lists of items where the lists just serve as a temporary container - which list types would you recommend me to use?

I

  • don't want to destroy the list manually
  • would like to use a built-in list type (no frameworks, libraries, ...)
  • want generics

Something which would make this possible without causing leaks:

function GetListWithItems: ISomeList;
begin
  Result := TSomeList.Create;
  // add items to list
end;

var
  Item: TSomeType;
begin
  for Item in GetListWithItems do
  begin
    // do something
  end;
end;

What options do I have? This is about Delphi 2009 but for the sake of knowledge please also mention if there is something new in this regard in 2010+.

like image 460
Heinrich Ulbricht Avatar asked Nov 10 '11 09:11

Heinrich Ulbricht


2 Answers

An (somehow ugly) workaround for this is to create an 'autodestroy' interface along with the list. It must have the same scope so that when the interface is released, your list is destroyed too.

type
  IAutoDestroyObject = interface
  end;

  TAutoDestroyObject = class(TInterfacedObject, IAutoDestroyObject)
  strict private
    FValue: TObject;
  public
    constructor Create(obj: TObject);
    destructor  Destroy; override;
  end;

constructor TAutoDestroyObject.Create(obj: TObject);
begin
  inherited Create;
  FValue := obj;
end; 

destructor TAutoDestroyObject.Destroy;
begin
  FreeAndNil(FValue);
  inherited;
end;

function CreateAutoDestroyObject(obj: TObject): IAutoDestroyObject;
begin
  Result := TAutoDestroyObject.Create(obj);
end; 

FList := TObjectList.Create;
FListAutoDestroy := CreateAutoDestroyObject(FList);

Your usage example gets more complicated, too.

type
  TSomeListWrap = record
    List: TSomeList;
    AutoDestroy: IAutoDestroyObject;
  end;

function GetListWithItems: TSomeListWrap;
begin
  Result.List := TSomeList.Create;
  Result.AutoDestroy := CreateAutoDestroyObject(Result.List);
  // add items to list
end;

var
  Item: TSomeItem;
begin
  for Item in GetListWithItems.List do
  begin
    // do something
  end;
end;
like image 167
gabr Avatar answered Sep 28 '22 00:09

gabr


Inspired by Barry Kelly's blog post here you could implement smart pointers for your purpose like this :

unit Unit80;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, Generics.Collections;

type
  TMyList =class( TList<Integer>)
  public
    destructor Destroy; override;
  end;

  TLifetimeWatcher = class(TInterfacedObject)
  private
    FWhenDone: TProc;
  public
    constructor Create(const AWhenDone: TProc);
    destructor Destroy; override;
  end;

  TSmartPointer<T: class> = record
  strict private
    FValue: T;
    FLifetime: IInterface;
  public
    constructor Create(const AValue: T); overload;
    class operator Implicit(const AValue: T): TSmartPointer<T>;
    property Value: T read FValue;
  end;

  TForm80 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    function getList : TSmartPointer<TMyList>;
    { Public declarations }
  end;


var
  Form80: TForm80;

implementation


{$R *.dfm}



{ TLifetimeWatcher }

constructor TLifetimeWatcher.Create(const AWhenDone: TProc);
begin
  FWhenDone := AWhenDone;
end;

destructor TLifetimeWatcher.Destroy;
begin
  if Assigned(FWhenDone) then
    FWhenDone;
  inherited;
end;

{ TSmartPointer<T> }

constructor TSmartPointer<T>.Create(const AValue: T);
begin
  FValue := AValue;
  FLifetime := TLifetimeWatcher.Create(procedure
  begin
    AValue.Free;
  end);
end;

class operator TSmartPointer<T>.Implicit(const AValue: T): TSmartPointer<T>;
begin
  Result := TSmartPointer<T>.Create(AValue);
end;

procedure TForm80.Button1Click(Sender: TObject);
 var i: Integer;
begin
  for I in getList.Value do
   Memo1.Lines.Add(IntToStr(i));

end;

{ TMyList }

destructor TMyList.Destroy;
begin
  ShowMessage('Kaputt');
  inherited;
end;

function TForm80.getList: TSmartPointer<TMyList>;
var
  x: TSmartPointer<TMyList>;
begin
  x := TMyList.Create;
  Result := x;
  with Result.Value do
  begin
    Add(1);
    Add(2);
    Add(3);
  end;
end;

end.

Look at getList and Button1click to see its usage.

like image 26
iamjoosy Avatar answered Sep 27 '22 22:09

iamjoosy