What lists can serve as temporary lists?

When working with lists of items where lists simply serve as a temporary container - what types of lists would you recommend to me?

I

  • no need to destroy the list manually
  • I would like to use the list type built-in (without frameworks, libraries, ...)
  • want generators

Something that 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 Delphi 2009, but for the sake of knowledge, please also indicate if there is anything new in this regard in 2010.

+7
source share
7 answers

Standard list classes, such as TList , TInterfaceList , TInterfaceList , etc., do not implement automatic life cycles, so you must release them manually when you are done with them. If you want the list class to be accessible through the interface, you must implement this yourself, for example:

 type IListIntf = interface ... end; TListImpl = class(TInterfacedObject, IListIntf) private FList: TList; ... public constructor Create; override; destructor Destroy; override; ... end; constructor TListImpl.Create; begin inherited; FList := TList.Create; end; destructor TListImpl.Destroy; begin FList.Free; inherited; end; function GetListWithItems: IListIntf; begin Result := TListImpl.Create; // add items to list end; 
+3
source

A workaround (somewhat ugly) for this is to create an "autodestroy" interface along with the list. It should have the same scope so that when the interface is released, your list will also be destroyed.

 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 use case is also becoming more complex.

 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; 
+6
source

Inspired by Barry Kelly's blog post here, you could implement smart pointers for your purposes as follows:

 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. 

Take a look at getList and Button1click to see its use.

+5
source

To fully support what you need, the language must support 2 things:

  • Garbage collector. This is the only thing that gives you the freedom to USE something without worrying about releasing it. I would welcome changes in Delphi that even gave us partial support for this.
  • Ability to define local initialized variables. Again, I would love to see something like that.

Meanwhile, closest you can use interfaces instead of garbage collection (since interfaces are counted by reference, as soon as they go out of scope they will be released). Regarding initialized local variables, you can use a trick similar to what I describe here: Declaring block level variables for branches in delphi

And for fun, here's a console application that demonstrates the use of "fake" local variables and interfaces to get temporary lists that are easy to initialize, will be automatically released:

 program Project1; {$APPTYPE CONSOLE} uses SysUtils, Generics.Collections; type ITemporaryLocalVar<T:constructor> = interface function GetL:T; property L:T read GetL; end; TTemporaryLocalVar<T:constructor> = class(TInterfacedObject, ITemporaryLocalVar<T>) public FL: T; constructor Create; destructor Destroy;override; function GetL:T; end; TTempUse = class public class function L<T:constructor>: ITemporaryLocalVar<T>; end; { TTemporaryLocalVar<T> } constructor TTemporaryLocalVar<T>.Create; begin FL := T.Create; end; destructor TTemporaryLocalVar<T>.Destroy; begin TObject(FL).Free; inherited; end; function TTemporaryLocalVar<T>.GetL: T; begin Result := FL; end; { TTempUse } class function TTempUse.L<T>: ITemporaryLocalVar<T>; begin Result := TTemporaryLocalVar<T>.Create; end; var i:Integer; begin try with TTempUse.L<TList<Integer>> do begin L.Add(1); L.Add(2); L.Add(3); for i in L do WriteLn(i); end; ReadLn; except on E: Exception do Writeln(E.ClassName, ': ', E.Message); end; end. 
+4
source

Another option is to implement the universal IEnumerable adapter (as one way to satisfy the for .. in compiler requirement) and rely on interface reference counting. I don't know if it works in Delphi 2009 while working in Delphi XE:

 program Project1; {$APPTYPE CONSOLE} uses SysUtils, Classes, Generics.Collections; type // IEnumerator adapter for TEnumerator TInterfacedEnumerator<T> = class(TInterfacedObject, IEnumerator<T>) private FEnumerator: TEnumerator<T>; public constructor Create(AEnumerator: TEnumerator<T>); destructor Destroy; override; function IEnumerator<T>.GetCurrent = GetCurrent2; { IEnumerator } function GetCurrent: TObject; function MoveNext: Boolean; procedure Reset; { IEnumerator<T> } function GetCurrent2: T; end; // procedure used to fill the list TListInitProc<T> = reference to procedure(List: TList<T>); // IEnumerable adapter for TEnumerable TInterfacedEnumerable<T> = class(TInterfacedObject, IEnumerable<T>) private FEnumerable: TEnumerable<T>; public constructor Create(AEnumerable: TEnumerable<T>); destructor Destroy; override; class function Construct(InitProc: TListInitProc<T>): IEnumerable<T>; function IEnumerable<T>.GetEnumerator = GetEnumerator2; { IEnumerable } function GetEnumerator: IEnumerator; overload; { IEnumerable<T> } function GetEnumerator2: IEnumerator<T>; overload; end; { TInterfacedEnumerator<T> } constructor TInterfacedEnumerator<T>.Create(AEnumerator: TEnumerator<T>); begin inherited Create; FEnumerator := AEnumerator; end; destructor TInterfacedEnumerator<T>.Destroy; begin FEnumerator.Free; inherited Destroy; end; function TInterfacedEnumerator<T>.GetCurrent: TObject; begin Result := TObject(GetCurrent2); end; function TInterfacedEnumerator<T>.GetCurrent2: T; begin Result := FEnumerator.Current; end; function TInterfacedEnumerator<T>.MoveNext: Boolean; begin Result := FEnumerator.MoveNext; end; procedure TInterfacedEnumerator<T>.Reset; begin // ? end; { TInterfacedEnumerable<T> } class function TInterfacedEnumerable<T>.Construct(InitProc: TListInitProc<T>): IEnumerable<T>; var List: TList<T>; begin List := TList<T>.Create; try if Assigned(InitProc) then InitProc(List); Result := Create(List); except List.Free; raise; end; end; constructor TInterfacedEnumerable<T>.Create(AEnumerable: TEnumerable<T>); begin inherited Create; FEnumerable := AEnumerable; end; destructor TInterfacedEnumerable<T>.Destroy; begin FEnumerable.Free; inherited Destroy; end; function TInterfacedEnumerable<T>.GetEnumerator: IEnumerator; begin Result := GetEnumerator2; end; function TInterfacedEnumerable<T>.GetEnumerator2: IEnumerator<T>; begin Result := TInterfacedEnumerator<T>.Create(FEnumerable.GetEnumerator); end; type TSomeType = record X, Y: Integer; end; function GetList(InitProc: TListInitProc<TSomeType>): IEnumerable<TSomeType>; begin Result := TInterfacedEnumerable<TSomeType>.Construct(InitProc); end; procedure MyInitList(List: TList<TSomeType>); var NewItem: TSomeType; I: Integer; begin for I := 0 to 9 do begin NewItem.X := I; NewItem.Y := 9 - I; List.Add(NewItem); end; end; procedure Main; var Item: TSomeType; begin for Item in GetList(MyInitList) do // you could also use an anonymous procedure here Writeln(Format('X = %d, Y = %d', [Item.X, Item.Y])); Readln; end; begin try ReportMemoryLeaksOnShutdown := True; Main; except on E: Exception do begin ExitCode := 1; Writeln(Format('[%s] %s', [E.ClassName, E.Message])); end; end; end. 
+2
source

No, not out of the box in Delphi. I know that you do not need a library, but you can be sure of the principle of TDynArray .

+1
source

There is a Guard function in the Jedi code library that already implements what Gabr Code does.

+1
source

All Articles