I'd like to define an array type which consists of different types, such as String
, Integer
, Boolean
, Double
, etc. but no objects, structures, or anything of that nature. Then I'd like to use this type as a function argument, for example...
type
TMyArray = array of ...?...;
function GetSomething(const Input: TMyArray): String;
var
X: Integer;
begin
for X:= 0 to Length(Input) - 1 do begin
//Identify type and handle accordingly...
//Serialize data for the result...
end;
end;
and use it like...
Variable:= GetSomething(['some string', 123, 'something else', 12.3, false]);
Then, how do I identify what type each element is when iterating through such an array?
I'm pretty sure this is possible, but have no idea even what terminology to search for. How do I do this?
Would I have to define this as an array of Variants? Or is there a way to define exactly which types the array accepts?
EDIT
Not to change the question any, but after the answer by RRUZ, I found an intriguing article about the performance when doing this exact thing different ways...
Arrays that contain instances of multiple types are often referred to as heterogenous arrays, and are incredibly commonly used in dynamically typed languages — like PHP, JavaScript, and Ruby.
To create an array type you can use Array<Type> type where Type is the type of elements in the array. For example, to create a type for an array of numbers you use Array<number> . You can put any type within Array<Type> .
The values in an array can be any type, so that arrays may contain values that are simple reals or integers, vectors, matrices, or other arrays.
In java, is it possible to create an array or any sort of collection which can hold different data types? Yes.
If your Delphi version supports RTTI, you can use an array of TValue
and the Kind
property like so.
{$APPTYPE CONSOLE}
uses
System.TypInfo,
System.Rtti,
System.SysUtils;
function GetSomething(const Input: array of TValue): String;
var
X: Integer;
LValue : TValue;
begin
for LValue in Input do begin
case LValue.Kind of
tkUnknown: Writeln('Unknown');
tkInteger: Writeln(Format('The Kind of the element is Integer and the value is %d',[LValue.AsInteger]));
tkChar: Writeln('Char');
tkEnumeration: if LValue.TypeInfo=TypeInfo(Boolean) then Writeln(Format('The Kind of the element is Boolean and the value is %s',[BoolToStr(LValue.AsBoolean, True)]));
tkFloat: Writeln(Format('The Kind of the element is Float and the value is %n',[LValue.AsExtended]));
tkString: Writeln('String');
tkSet: Writeln('Set');
tkClass: Writeln('Class');
tkMethod:Writeln('method');
tkWChar: Writeln('WChar');
tkLString: Writeln('String');
tkWString: Writeln('String');
tkVariant: Writeln('Variant');
tkArray: Writeln('Array');
tkRecord: Writeln('Record');
tkInterface: Writeln('Interface');
tkInt64: Writeln('Int64');
tkDynArray: Writeln('DynArray');
tkUString: Writeln(Format('The Kind of the element is String and the value is %s',[LValue.AsString]));
tkClassRef: Writeln('Class Ref');
tkPointer: Writeln('Pointer');
tkProcedure: Writeln('procedure');
end;
end;
end;
begin
try
GetSomething(['some string', 123, 'something else', 12.3, false]);
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
Readln;
end.
Another option is use an array of const
{$APPTYPE CONSOLE}
uses
SysUtils;
procedure GetSomething(const Input: array of const);
var
LIndex: Integer;
begin
for LIndex := Low(Input) to High(Input) do
begin
case Input[LIndex].VType of
vtWideString: Writeln('WideString = ''', WideString(Input[LIndex].VWideChar), '''');
vtInt64: Writeln('Int64 = ', Input[LIndex].VInt64^);
vtCurrency: Writeln('Currency = ', CurrToStr(Input[LIndex].VCurrency^));
vtInteger: Writeln('Integer = ', Input[LIndex].VInteger);
vtBoolean: Writeln('Boolean = ', BoolToStr(Input[LIndex].VBoolean, True));
vtChar: Writeln('Char = ''', Input[LIndex].VChar, '''');
vtExtended: Writeln('Extended = ', FloatToStr(Input[LIndex].VExtended^));
vtString: Writeln('ShortString = ''', Input[LIndex].VString^, '''');
vtPChar: Writeln('PChar = ''', Input[LIndex].VPChar, '''');
vtAnsiString: Writeln('AnsiString = ''', Ansistring(Input[LIndex].VAnsiString), '''');
vtWideChar: Writeln('WideChar = ''', Input[LIndex].VWideChar, '''');
vtPWideChar: Writeln('PWideChar = ''', Input[LIndex].VPWideChar, '''');
vtUnicodeString : Writeln('UnicodeString = ''', string(Input[LIndex].VUnicodeString), '''');
else
Writeln('Unsupported');
end;
end;
end;
begin
try
GetSomething(['some string', 123, 'something else', 12.3, false]);
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
Readln;
end.
Arrays are homogeneous. As the documentation says:
An array represents an indexed collection of elements of the same type (called the base type).
Therefore you can achieve your goal only by way of a base type that can hold differing types of data. Such a data type is known as a variant data type.
In Delphi there are a number of possibilities for variant data types. There is the venerable COM Variant
type. There is the new kid on the block, TValue
, which was added to support new style RTTI. And there are many third party options. Typically these third party options exist to support persistence frameworks.
Oddly enough, nobody has yet mentioned variant records, which have been a feature of Pascal for decades:
type
TVarRecType = (vrtInteger, vrtDouble {other types go here});
TVarRec = record
Field1: string; { can be omitted }
case RecType: TVarRecType of
vrtInteger:
IntValue: integer;
vrtDouble:
DblValue: double;
{ other cases go here }
end;
var
VarRec: TVarRecType;
begin
VarRec.Field1 := 'This is an example.';
VarRec.RecType := vrtInteger;
VarRec.IntValue := 4711;
{...}
VarRec.RecType := wrtDouble;
VarRec.DblValue := Pi;
{...}
end;
{ Oops, forgot the array part }
type
TVarRecArr = array[1..15] of TVarRec;
var
VarRecArr: TVarRecArr;
begin
VarRecArr[1].Field1 := 'This is the first record';
VarRecArr[1].RecType := wrtInteger;
VarRecArr[1].IntValue := 1;
{...}
end;
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