DynArraySize()仅适用于649个整数元素的数组

Ren*_*ann 3 delphi rtti dynamic-arrays delphi-10.2-tokyo

我在Delphi 10.2 Update 2中遇到了与RTTI相关的问题,并且能够将其跟踪到更少的代码量(见下文).

我有一些TPersistent-descendant类TMyObj发布类型的属性TArray<Integer>.当我通过GetDynArrayProp()它接收它的值并通过它查询它的大小时,DynArraySize()只能达到649个元素的大小.在此特殊计数之上返回一些非常大的值.

请注意,我的数组是从一个实例生成TDictionary<Integer,Boolean>Keys拥有自己的财产ToArray的方法.我也尝试修改,TMyObj.GetDynArray以便它TArray<Integer>直接返回一个实例,它正常工作.因此,我认为这可能会以一些神秘的方式相关联.

我的使用有DynArraySize()什么问题?这种动态数组的神秘行为背后是什么?

program RTTIPropDynArray;

{$APPTYPE CONSOLE}

uses
  System.Classes, System.Generics.Collections, System.SysUtils, System.TypInfo;

type
  TMyDict  = TDictionary<Integer,Boolean>;
  TMyArray = TArray<Integer>;

  TMyObj = class(TPersistent)
  private
    FValues: TMyDict;
    function GetDynArray: TMyArray;
  public
    constructor Create(const ACount: Integer);
    destructor Destroy; override;
  published
    property DynArray: TMyArray read GetDynArray;
  end;

{ TMyObj }

constructor TMyObj.Create(const ACount: Integer);
begin
  FValues := TMyDict.Create;
  while FValues.Count < ACount do
    FValues.AddOrSetValue(Random(MaxInt), False);
end;

destructor TMyObj.Destroy;
begin
  FreeAndNil(FValues);
  inherited;
end;

function TMyObj.GetDynArray: TMyArray;
begin
  Result := FValues.Keys.ToArray;
end;

function Test(const ACount: Integer): Boolean;
var
  LInstance: TMyObj;
  LExpectedSize: Integer;
  LDynArraySize: Integer;
begin
  LInstance := TMyObj.Create(ACount);
  try
    LExpectedSize := Length(LInstance.DynArray);
    LDynArraySize := DynArraySize(GetDynArrayProp(LInstance, 'DynArray'));
    Result := LExpectedSize = LDynArraySize;
    if not Result then
      WriteLn(Format('Expected size: %d; DynArraySize: %d', [LExpectedSize, LDynArraySize]));
  finally
    LInstance.Free;
  end;
end;

var
  LCount: Integer;
begin
  Randomize;
  LCount := 1;
  while Test(LCount) do
    Inc(LCount);
  ReadLn;
end.
Run Code Online (Sandbox Code Playgroud)

Ste*_*nke 8

简短的回答:你的代码坏了

答案很长:

吸气剂的呼叫创建一个新的阵列(见TEnumerable<T>.ToArrayImplSystem.Generics.Collections.pas),这是在的结尾被释放System.TypInfo.GetDynArrayProp(放一个断点,并期待入拆装-它显示@DynArrayClear).由于没有对此数组的其他引用,因此其内存将被释放(如果您System.pas进一步深入,您将看到它最终会被终止_FreeMem).这意味着每次调用此函数都会返回一个悬空指针!

现在为什么在之前的所有电话中都能得到正确的结果?巧合 - 记忆尚未被其他任何东西重新分配.

我想到两个可能的解决方案,不涉及重写getter:

  • 从使用RTTI System.Rtti.pasTValue保持基准活着
  • 写自己的版本GetDynArrayProp,保持基准活着-但你必须确保随时调用DynArrayClear后或创建内存泄漏

我个人会使用第一个.