如何在Delphi中将const的开放数组分配给TArray <tvarrec>

时间:2018-10-28 23:51:06

标签: delphi

我知道编译器将array of const“翻译”为array of TVarRec,所以我想知道是否存在一种存储副本的方法(或者可能是使用const的引用) )直接表示一个开放数组,我的意思是不进行循环将const数组中的每个项目复制到TArray<TVarRec>。以下代码显示了我正在尝试做的事情。

TAppMessage = class
private
  FMessage: string;
  FArgs: TArray<TVarRec>;
public
  constructor Create(AMessage: string; Args array of const);
  function ToString: string;
end;

constructor TAppMessage.Create(AMessage: string; Args array of const);
begin
  Self.FMessage := AMessage;
  Self.FArgs := Args; //<-- E2010 Incompatible types: 'System.TArray<System.TVarRec>' and 'array of TVarRec'
end;

function TAppMessage.ToString: string;
begin
  Result := Format(Self.FMessage, Self.FArgs);
end;

var AppMsg: TAppMessage;
AppMsg := TAppMessage.Create('A number %d and a text %s', [10, 'foo']);
ShowMessage(AppMsg.ToString); 

是否可以将一个开放的const数组分配给TArray<TVarRec>

注意:我正在使用Delphi 10.2.3(东京)

2 个答案:

答案 0 :(得分:4)

不要简单地复制TVarRecs!

see 看到了几个答案,建议您将TVarRec一一复制到TArray<TVarRec>中。 那还不够!

它将适用于可以直接存储在TVarRec中的值,例如Integer,但是不适用于不合适的值。这些被分配在堆栈上(换句话说:在临时存储中),然后从TVarRec引用。看一下TVarRec的声明:

type
  TVarRec = record { do not pack this record; it is compiler-generated }
    case Integer of
      0: (case Byte of
            vtInteger:       (VInteger: Integer);
            vtBoolean:       (VBoolean: Boolean);
            vtChar:          (VChar: _AnsiChr);
            vtExtended:      (VExtended: PExtended);
{$IFNDEF NEXTGEN}
            vtString:        (VString: _PShortStr);
{$ENDIF !NEXTGEN}
            vtPointer:       (VPointer: Pointer);
            vtPChar:         (VPChar: _PAnsiChr);
{$IFDEF AUTOREFCOUNT}
            vtObject:        (VObject: Pointer);
{$ELSE}
            vtObject:        (VObject: TObject);
{$ENDIF}
            vtClass:         (VClass: TClass);
            vtWideChar:      (VWideChar: WideChar);
            vtPWideChar:     (VPWideChar: PWideChar);
            vtAnsiString:    (VAnsiString: Pointer);
            vtCurrency:      (VCurrency: PCurrency);
            vtVariant:       (VVariant: PVariant);
            vtInterface:     (VInterface: Pointer);
            vtWideString:    (VWideString: Pointer);
            vtInt64:         (VInt64: PInt64);
            vtUnicodeString: (VUnicodeString: Pointer);
         );
      1: (_Reserved1: NativeInt;
          VType:      Byte;
         );
  end;

如您所见,CurrencyVariantShortString之类的类型显然不适合TVarRec,因此它们被引用 (并且真实值在运行时被放置在堆栈上)。如果您仅复制TVarRec,则不会复制值,并且由于这些值仅在通话期间有效,因此该引用将在例程中尽快无效结束。因此,您必须将这些引用的值复制到自己的存储中,例如:

SetLength(Copies, Length(Values));
for I := 0 to High(Values) do
begin
  Copies[I] := TVarRec(Values[I]);
  case TVarRec(Values[I]).VType of
  ...
    vtExtended:
      begin
        // Copy the referenced Extended to the heap:
        New(Copies[I].VExtended);
        Copies[I].VExtended^ := TVarRec(Values[I]).VExtended^;
      end;
    // etc...

等...您还必须注意UnicodeStringAnsiStringVariant,接口等的正确刷新。

所以不要听从那些告诉您复制阵列的人的建议

您必须确实制作一个深层副本,如我显示的in an article I have written exactly about this所示。我无法引用整篇文章,但您可以在我的网站上找到它。

警告

不仅复制代码,还请阅读本节的其余部分,因为使用后必须使用此处显示的功能释放复制数组引用的值。

答案 1 :(得分:1)

当该方法返回时,传递给TAppMessage.Create的数组参数变得无效。因此,出于终身原因,您无法保存参考。

您应该复制一份。