I need to store a temporary list of records and was thinking that a TList
would be a good way to do this? However I am unsure how to do this with a TList
and was wondering if this is the best was and also if anyone has any examples of how to do this?
The easiest way is to create your own descendant of TList
. Here's a quick sample console app to demonstrate:
program Project1;
{$APPTYPE CONSOLE}
uses
SysUtils, Classes;
type
PMyRec=^TMyRec;
TMyRec=record
Value: Integer;
AByte: Byte;
end;
TMyRecList=class(TList)
private
function Get(Index: Integer): PMyRec;
public
destructor Destroy; override;
function Add(Value: PMyRec): Integer;
property Items[Index: Integer]: PMyRec read Get; default;
end;
{ TMyRecList }
function TMyRecList.Add(Value: PMyRec): Integer;
begin
Result := inherited Add(Value);
end;
destructor TMyRecList.Destroy;
var
i: Integer;
begin
for i := 0 to Count - 1 do
FreeMem(Items[i]);
inherited;
end;
function TMyRecList.Get(Index: Integer): PMyRec;
begin
Result := PMyRec(inherited Get(Index));
end;
var
MyRecList: TMyRecList;
MyRec: PMyRec;
tmp: Integer;
begin
MyRecList := TMyRecList.Create;
for tmp := 0 to 9 do
begin
GetMem(MyRec, SizeOf(TMyRec));
MyRec.Value := tmp;
MyRec.AByte := Byte(tmp);
MyRecList.Add(MyRec);
end;
for tmp := 0 to MyRecList.Count - 1 do
Writeln('Value: ', MyRecList[tmp].Value, ' AByte: ', MyRecList[tmp].AByte);
WriteLn(' Press Enter to free the list');
ReadLn;
MyRecList.Free;
end.
This eliminates a couple of things:
As Remy and Warren both said, it's a little more work because you have to allocate the memory when you add new records.
First, if you want to combine a classic TList with Records, you will need to:
Combining Lists with Records requires so much "pointers-and-heap-management" work that such a technique would be only within the capabilities of an expert.
Alternatives to what you have asked for that still use something called "TList", include using a generics.collections style TList, with Record types, which would have all the benefits of TList, but would require you to basically do a lot of entire-record-copies to get data into it.
The most idiomatic Delphi ways to do what you ask are to either:
use a TList or TObjectList with a Class Types instead of a record. Usually you end up subclassing either TList or TObjectList in this case.
Use a dynamic Array of Record Types, but be aware that it's harder to sort an Array type, and that expanding an array type at runtime isn't as speedy as it is with a TList.
Use generics.Collections TList with your classes. This lets you avoid subclassing TList or TObjectList each time you want to use a list with a different class.
A code sample showing Dynamic arrays:
TMyRec = record
///
end;
TMyRecArray = array of TMyRec;
procedure Demo;
var
myRecArray:TMyRecArray;
begin
SetLength(myRecArray,10);
end;
Now for some background information on why TList is not easy to use with Record types:
TList is better suited for use with Class types, because a variable of type 'TMyClass', where 'type TMyClass = class .... end;' can be easily "referred to" as a pointer value, which is what TList holds.
Variables of type Record are value-Types in Delphi, whereas class values are implicitly by-reference values. You can think of by-reference values as stealth-pointers. You don't have to dereference them to get at their contents, but when you add it to a TList, you're actually just adding a pointer to the TList, not making a copy or allocating any new memory.
The answer by Remy shows you literally you how to do exactly what you want, and I am writing my answer only because I want to warn you about the details of what you are asking, and suggest that you consider alternatives too.
You can take a look at our TDynArray wrapper. It's defined in an Open Source unit, working from Delphi 6 up to XE.
With TDynArray
, you can access any dynamic array (like TIntegerDynArray = array of integer
or TRecordDynArray = array of TMyRecord
) using TList
-like properties and methods, e.g. Count, Add, Insert, Delete, Clear, IndexOf, Find, Sort
and some new methods like LoadFromStream, SaveToStream, LoadFrom
and SaveTo
which allow fast binary serialization of any dynamic array, even containing strings or records - a CreateOrderedIndex
method is also available to create individual index according to the dynamic array content. You can also serialize the array content into JSON, if you wish. Slice, Reverse
or Copy
methods are also available.
It will handle a dynamic array of records, and even records within records, with strings or other dynamic arrays inside.
When using an external Count
variable, you can speed up a lot the adding of elements in the referred dynamic array.
type
TPerson = packed record
sCountry: string;
sFullName: string;
sAddress: string;
sCity: string;
sEmployer: string;
end;
TPersons = array of TPerson;
var
MyPeople: TPersons;
(...)
procedure SavePeopleToStream(Stream: TMemoryStream);
var aPeople: TPerson;
aDynArray: TDynArray;
begin
aDynArray.Init(TypeInfo(TPersons),MyPeople);
aPeople.sCountry := 'France';
aPeople.sEmployer := 'Republique';
aDynArray.Add(aPeople);
aDynArray.SaveToStream(Stream);
end; // no try..finally Free needed here
There is also a TDynArrayHashed
class, which allow internal hashing of a dynamic array content. It's very fast and able to hash any kind of data (there are standard hashers for strings, but you can supply your own - even the hash function can be customized).
Note that TDynArray
and TDynArrayHashed
are just wrappers around an existing dynamic array variable. You can therefore initialize a TDynArray
wrapper on need, to access more efficiently any native Delphi dynamic array.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With