【问题标题】:Can I define an array type of different types?我可以定义不同类型的数组类型吗?
【发布时间】:2013-09-18 01:34:07
【问题描述】:

我想定义一个由不同类型组成的数组类型,例如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 的回答之后,我发现an intriguing article 以不同的方式做这件事时的性能......

【问题讨论】:

标签: arrays delphi delphi-xe2


【解决方案1】:

如果你的 Delphi 版本支持 RTTI,你可以像这样使用 TValueKind 属性的数组。

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

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

【讨论】:

  • 现在除了它是 RTTI 之外,我还必须弄清楚使用 this 或 Variant 之间有什么区别
  • 说到这里,相关阅读:thedelphigeek.com/2010/03/…
  • +1。我正在用 VType 替代方案写一个答案,并注意到您在发布之前已经进行了编辑。刷新了您的答案,不得不放弃我的答案。我也不能对这两个选项都投两次票。 :-)
  • 我只需要说清楚 - 我可以将这些定义为它们自己的类型并将这些类型用作参数吗?还是我必须按照您在答案中的方式定义这样的数组,在方法中嵌入?
  • 还有VarArrayOf (array of Variant),但由于您希望能够根据所持物品的类型执行不同的选项,那么array of const 可能是您更好的选择(不是很幸运(?)足以在具有 TValue 的现代 Delphi 上工作,因此无法发表评论)。但是请记住,这些值不是持久的(如果要将数组保留为 const 数组并且超出范围,则需要管理自己的内存)。一篇谈论这个的好文章是rvelthuis.de/articles/articles-openarr.html
【解决方案2】:

奇怪的是,还没有人提到变体记录,这几十年来一直是 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+SizeOf(enum) 字节。这可能很重要,具体取决于数组的大小。
【解决方案3】:

数组是同构的。正如documentation 所说:

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

因此,您只能通过可以容纳不同类型数据的基本类型来实现您的目标。这种数据类型称为variant data type

在 Delphi 中,变体数据类型有多种可能性。有古老的 COM Variant 类型。块上有一个新的孩子,TValue,它被添加以支持新样式的 RTTI。并且有许多第三方选项。通常存在这些第三方选项以支持持久性框架。

【讨论】:

    【解决方案4】:

    由于用作参数,您可以使用array of const 构造。 也称为变体开放数组参数More on my answer on this other question.

    它就像您想要的不同类型的数组一样工作。 Delphi docwiki documentation on that theme

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-06-24
      • 2020-07-02
      • 2021-03-23
      • 2016-01-12
      • 2010-10-22
      • 2019-07-11
      • 1970-01-01
      相关资源
      最近更新 更多