我可以为不同类型定义一个数组类型吗?

10

我希望定义一个由不同类型组成的数组类型,例如StringIntegerBooleanDouble等,但没有对象、结构或任何类似的东西。然后我想将此类型用作函数参数,例如...

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;

并像这样使用它...

Variable:= GetSomething(['some string', 123, 'something else', 12.3, false]);

那么,当我遍历这样的数组时,如何确定每个元素的类型?

我相信这是可能的,但甚至不知道要搜索什么术语。我该怎么做?

我需要将其定义为变量数组吗?还是有一种方法可以定义数组接受的确切类型?

编辑

并不是要改变问题,但在RRUZ的答案之后,我发现一篇有趣的文章,介绍了执行此操作的不同方式的性能比较...


TValue 是什么? - RRUZ
RTTI?我从来没有真正使用过它。 - Jerry Dodge
现在提到TValue:https://dev59.com/questions/3EvSa4cB1Zd3GeqPfZCO - Jerry Dodge
1
据我所知,Delphi XE2 中 TValue 的性能得到了改善。 - RRUZ
6个回答

13

如果你的Delphi版本支持RTTI,你可以使用一个TValue数组和Kind属性,像这样。

{$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.

另一种选择是使用一个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.

1
现在除了它是RTTI之外,我还要弄清楚使用这个或Variant的区别。 - Jerry Dodge
说到这个,这里有一个相关阅读链接:http://www.thedelphigeek.com/2010/03/speed-comparison-variant-tvalue-and.html - Jerry Dodge
+1. 我正在使用VType替代方案写答案,但注意到你在发布前进行了编辑。刷新了你的答案后,我不得不放弃我的答案。我也不能投两票,因为这个问题包含了两个选项。 :-) - Ken White
我需要澄清一下 - 我可以将它们定义为自己的类型并将类型用作参数吗?还是必须像您在答案中所做的那样,在方法中内联定义这些数组? - Jerry Dodge
还有VarArrayOfVariant数组),但是如果您想根据所持物的类型执行不同的选项,那么const数组可能是更好的选择(没有幸运地在拥有TValue的现代Delphi上工作,因此无法发表评论)。但请记住,这些值不是持久的(如果您想将数组保留为const数组并且超出范围,则需要管理自己的内存)。关于这个很好的文章是http://rvelthuis.de/articles/articles-openarr.html。 - Matt Allwood

3

奇怪的是,还没有人提到变体记录,这在几十年前Pascal中就已经存在了:

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;

@Rob:这没问题。我实际上已经忘记了它。它可能支持太多类型,不适用于特定的用例,例如,如果您只想存储整数和单精度浮点数,则自定义变体记录的大小仅为4 +枚举的大小。这可能会影响到您的数组大小。 - dummzeuch

1

数组是同质的。如文档所述:

数组代表了一个相同类型元素的索引集合(称为基类型)。

因此,您只能通过可以保存不同类型数据的基类型来实现目标。这种数据类型称为变量数据类型

在Delphi中,有许多可能性用于变量数据类型。有备受尊敬的COM Variant类型。有一个新的、名声尚未显赫的TValue,它被添加以支持新式运行时类型信息(RTTI)。还有许多第三方选项。通常这些第三方选项存在是为了支持持久性框架。


0

这很容易,而且在Delphi的不同版本之间没有区别

首先,您需要一个名称和类型的记录,例如:

Customer = record
OINTCUSTID      : INTEGER ;
CUSTTYPE        : SmallInt;
NAME            : string[30];
end;

现在你的数组变成了这样:

Glb_CUSTOMER       :ARRAY [1..20] OF Customer;

现在你有一个具有不同类型的数组。


0
假设最终你想要以字符串形式获得数组输出(使用过程 GetSomething)。我认为你可以很容易地使用Variants来实现这一点。
像这样定义你的数组:
MyArray = array of variant;

GetSomething 过程 现在变得简单了:

function TForm3.GetSomething(const Input: TMyArray): String;
var
  X: Integer;
begin
  for X:= 0 to Length(Input) - 1 do
    //Identify type and handle accordingly...
    //Serialize data for the result...
    Result := Result + VarToStrDef(Input[X], '?') + ' | ';
end;

结果符合预期。
这行代码:

Variable:= GetSomething(['some string', 123, 'something else', 12.3, false]);

返回这个结果:

一些字符串 | 123 | 其他东西 | 12,3 | 假 |


0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接