I'm trying to convert an array of T
into a Variant
(varArray
).
With not-generic types (i.e: Integer
), I'm using the following function:
function ToVarArray(AValues : array of Integer) : Variant;
var
i : integer;
begin
Result := VarArrayCreate(
[Low(AValues), High(AValues)],
varInteger
);
for i := Low(AValues) to High(AValues) do
Result[i] := AValues[i];
end;
I'm having some problems while trying to do the same thing with a generic TArray
:
uses
System.Generics.Collections;
type
TArray = class(System.Generics.Collections.TArray)
public
class function ToVarArray<T>(const AValues: array of T) : Variant; static;
end;
I've tried the following:
class function TArray.ToVarArray<T>(const AValues: array of T) : Variant;
var
i : integer;
Tmp : T;
begin
Result := Tmp;
Result := VarArrayCreate(
[Low(AValues), High(AValues)],
VarType(Result)
);
for i := Low(AValues) to High(AValues) do
Result[i] := AValues[i];
end;
But It produces the following compile error at each row where I'm assigning a T
to a Variant
:
[dcc32 Error] Unit1.pas(36): E2010 Incompatible types: 'Variant' and 'T'
The title of the question says that you'd like to process generic TArray<T>
, but the first sentence say it's array of T
. You might think that both terms refer to the same data structure (dynamic array), and most of the time they do, but they make difference if you use them in place of a procedure/function argument declaration.
Therefore the following methods have different signatures and accept different types of parameters:
class function TArray.ToVarArray<T>(const AValues: TArray<T>): Variant;
class function TArray.ToVarArray<T>(const AValues: array of T): Variant;
While the first invariant accepts true dynamic array as a parameter, the latter takes an open array. This unfortunate language design is regular source of confusion for Delphi developers.
Delphi RTL already contains function that converts dynamic array to variant array of appropriate type - DynArrayToVariant
. It takes pointer to initial element of dynamic array and array's type information as its arguments. To make use of generics you would write:
uses
System.Variants;
class function TArray.DynArrayToVarArray<T>(const AValues: TArray<T>): Variant;
begin
DynArrayToVariant(Result, @AValues[0], TypeInfo(TArray<T>));
end;
Your code would fail to compile if you try to use this routine with a static array:
var
SA: array[0..2] of Integer;
begin
SA[0] := 0;
SA[1] := 1;
SA[2] := 2;
{ E2010 Incompatible types: 'System.TArray<System.Integer>' and 'array[0..2] of Integer' }
TArray.DynArrayToVarArray<Integer>(SA);
end.
Open array solves this issue, but you need to "convert" it to dynamic array in order to use it with RTL's DynArrayToVariant
.
class function TArray.OpenArrayToVarArray<T>(const AValues: array of T): Variant;
var
LArray: TArray<T>;
Index: Integer;
begin
SetLength(LArray, Length(AValues));
for Index := Low(AValues) to High(AValues) do
LArray[Index] := AValues[index];
Result := DynArrayToVarArray<T>(LArray);
end;
var
DA: TArray<Integer>;
SA: array[0..2] of Integer;
begin
DA := [0, 1, 2];
SA[0] := 0;
SA[1] := 1;
SA[2] := 2;
{ these all work }
TArray.OpenArrayToVarArray<Integer>(DA); // dynamic array
TArray.OpenArrayToVarArray<Integer>(SA); // static array
TArray.OpenArrayToVarArray<Integer>([0, 1, 2]); // open array constructor
end.
The above OpenArrayToVarArray<T>
routine is pretty ineffective both in terms of performance and memory usage, because it copies elements one by one from open array to dynamic array. If you really need to support open arrays you should probably write better implementation inspired by RTL's DynArrayToVariant
, however I find that one to be sub-optimal too.