Appending one element to a dynamic array
Here's a hack with generics which only works with TArray<T>
:
type
TAppender<T> = class
class procedure Append(var Arr: TArray<T>; Value: T);
end;
class procedure TAppender<T>.Append;
begin
SetLength(Arr, Length(Arr)+1);
Arr[High(Arr)] := Value;
end;
Usage:
var
TestArray: TArray<Integer>;
begin
TAppender<Integer>.Append(TestArray, 5);
end.
Every time you call SetLength
the memory gets reallocated. Maybe the entire array needs to be copied to a different location. And you who just wanted to add a single element to the array!
Basically: never do this. There are two ways out of it. The simplest case is if you beforehand know the maximum size of the array:
procedure Example1;
var
data: array of string;
ActualLength: integer;
procedure AddElement(const Str: string);
begin
data[ActualLength] := Str;
inc(ActualLength);
end;
begin
ActualLength := 0;
SetLength(data, KNOWN_UPPER_BOUND);
for ...
while ...
repeat ...
AddElement(SomeString);
SetLength(data, ActualLength);
end;
Here is a practial example of this approach.
If you don't know any upper bound a priori, then allocate in large chunks:
procedure Example2;
const
ALLOC_BY = 1024;
var
data: array of string;
ActualLength: integer;
procedure AddElement(const Str: string);
begin
if ActualLength = length(data) then
SetLength(data, length(data) + ALLOC_BY);
data[ActualLength] := Str;
inc(ActualLength);
end;
begin
ActualLength := 0;
SetLength(data, ALLOC_BY);
for ...
while ...
repeat ...
AddElement(SomeString);
SetLength(data, ActualLength);
end;
This second approach is implemented in the run-time library's TList<T>
, TObjectList<T>
, TStringList
etc. Hence, when you use these classes, it is perfectly fine to append the list one item at a time.
Starting with Delphi XE7 you can do:
SomeDynamicArray := SomeDynamicArray + [NewElement];
ref: Marco Tech Blog, September 18, 2014 : Dynamic Arrays in Delphi XE7