如何获取给定变体的实际类型的内存大小?

How to get the memory size of the actual type of a given variant?

考虑以下代码:

procedure Test;
var
  IntGenuine: UInt64;
  IntVariant: Variant;

begin
  IntGenuine := 0;
  IntVariant := UInt64(0); // The type of the variant is UInt64 now

  WriteLn('Size of IntGenuine = ', SizeOf(IntGenuine)); // Output: 8
  WriteLn('Size of IntVariant = ', SizeOf(IntVariant)); // Output: 24
end;

我知道语句 SizeOf(IntVariant) 等价于 SizeOf(Variant)。它获取类型 Variant 的大小,而不是变体实际类型的大小(在本例中为 UInt64)。

如何获取给定变体的实际类型的内存大小?

如果您的变量是 variant,它将在 Win32 下使用(至少)16 个字节,在 Win64 下使用 24 个字节,无论存储什么值

这个变量的内存大小总是variant结构之一,在System.pas中定义为TVarData .

正在定义:

var
  IntVariant: Variant;

实际上与定义相同:

var
  IntVariant: TVarData;

有一些initialization/finalization魔法:

var
  IntVariant: TVarData;
begin
  IntVariant.VType := varEmpty;
  try
    ...
  finally
    VarClear(variant(IntVariant));
  end;
end;

如果您不存储任何内容(varEmptyvarNull),它仍将使用 16/24 字节。如果您存储 boolean,它仍将使用 16/24 字节。如果它存储一些 string,则必须将堆分配的存储文本值添加到 16/24 字节。

您可以创建这样的函数:

Program Project1;

{$APPTYPE CONSOLE}
uses
  Variants, SysUtils;

function GetVarTypeSize(AVarType : TVarType; var isArray : boolean) : integer;
begin

  isArray := AVarType <> (AVarType and VarTypeMask);    

  case AVarType and VarTypeMask of
    varSmallInt: result := SizeOf(SmallInt);
    varInteger:  result := SizeOf(Integer);
    varSingle:   result := SizeOf(Single);
    varDouble:   result := SizeOf(Double);
    varCurrency: result := SizeOf(Currency);
    varDate:     result := SizeOf(TDateTime);
    varOleStr:   result := SizeOf(PWideChar);
    varDispatch: result := SizeOf(Pointer);
    varError:    result := SizeOf(HRESULT);
    varBoolean:  result := SizeOf(WordBool);
    varUnknown:  result := SizeOf(Pointer);
    varShortInt: result := SizeOf(ShortInt);
    varByte:     result := SizeOf(Byte);
    varWord:     result := SizeOf(Word);
    varLongWord: result := SizeOf(LongWord);
    varInt64:    result := SizeOf(Int64);
    varUInt64:   result := SizeOf(UInt64);
    varString:   result := SizeOf(Pointer);
    varAny:      result := SizeOf(Pointer);
    varArray:    result := SizeOf(PVarArray);
    varByRef:    result := SizeOf(Pointer);
    varUString:  result := SizeOf(Pointer);
    varRecord:   result := SizeOf(TVarRecord);
  else
    result := -1;  //unknown
  end;
end;

var
  v : Variant;
  b : boolean;
begin
  v := 3.141592654;   // double
  Write(GetVarTypeSize(VarType(v), b));
  if b then WriteLn(' : Is array') else WriteLn;

  v := 3;             // byte
  Write(GetVarTypeSize(VarType(v), b));
  if b then WriteLn(' : Is array') else WriteLn;

  v := integer(3);    // integer
  Write(GetVarTypeSize(VarType(v), b));
  if b then WriteLn(' : Is array') else WriteLn;

  v := Now;           // DateTime
  Write(GetVarTypeSize(VarType(v), b));
  if b then WriteLn(' : Is array') else WriteLn;

  v := VarArrayCreate([0,9], varDouble);   //array ! careful
  Write(GetVarTypeSize(VarType(v), b));
  if b then WriteLn(' : Is array') else WriteLn;

  ReadLn;
end.

此处VarTypeMask将屏蔽定义变体数组的位。如果基本变体是数组类型,则屏蔽它会告诉您数组元素的类型。

您可以阅读更多内容in the documentation

变体在幕后存储在 TVarData 类型的记录中。

System.TVarData Record:

PVarData = ^TVarData;
TVarData = packed record
  case Integer of
    0: (VType: TVarType;
        case Integer of
          0: (Reserved1: Word;
              case Integer of
                0: (Reserved2, Reserved3: Word;
                    case Integer of
                      varSmallInt: (VSmallInt: SmallInt);
                      varInteger:  (VInteger: Integer);
                      varSingle:   (VSingle: Single);
                      varDouble:   (VDouble: Double);
                      varCurrency: (VCurrency: Currency);
                      varDate:     (VDate: TDateTime);
                      varOleStr:   (VOleStr: PWideChar);
                      varDispatch: (VDispatch: Pointer);
                      varError:    (VError: HRESULT);
                      varBoolean:  (VBoolean: WordBool);
                      varUnknown:  (VUnknown: Pointer);
                      varShortInt: (VShortInt: ShortInt);
                      varByte:     (VByte: Byte);
                      varWord:     (VWord: Word);
                      varLongWord: (VLongWord: LongWord);
                      varInt64:    (VInt64: Int64);
                      varUInt64:   (VUInt64: UInt64);
                      varString:   (VString: Pointer);
                      varAny:      (VAny: Pointer);
                      varArray:    (VArray: PVarArray);
                      varByRef:    (VPointer: Pointer);
                      varUString:  (VUString: Pointer);
                      varRecord:   (VRecord: TVarRecord);
                      //$ffff:     (VLargest: TLargestVarData);
                   );
                1: (VLongs: array[0..{$IFDEF CPUX64}4{$ELSE}2{$ENDIF}] of LongInt);
             );
          2: (VWords: array [0..{$IFDEF CPUX64}10{$ELSE}6{$ENDIF}] of Word);
          3: (VBytes: array [0..{$IFDEF CPUX64}21{$ELSE}13{$ENDIF}] of Byte);
        );
    1: (RawData: array [0..{$IFDEF CPUX64}5{$ELSE}3{$ENDIF}] of LongInt);
end;

Variant变量转换为这种类型可以获得您正在寻找的信息:

var
  varData: TVarData;
  intVariant: Variant;
  size: Integer;
begin
  intVariant := UInt64(10);
  varData := TVarData(IntVariant);

  case varData.VType of
    varUInt64: size := SizeOf(varData.VUInt64);
    varInteger: size := SizeOf(varData.VInteger);
  . . .
  end;
end;

...但是以上不是通常的做法:

var
  intVariant: Variant;
  size: Integer;
  vType: Integer;
begin     
  vType := VarType(intVariant) and VarTypeMask;
  case vType of
    varUInt64: size := SizeOf(UInt64);
    varInteger: size := SizeOf(Integer);
    . . .
  end;
end;