delphi - DynArraySize() 仅能正确运行最多 649 个整数元素的数组

标签 delphi dynamic-arrays rtti delphi-10.2-tokyo

我在 Delphi 10.2 Update 2 中遇到了与 RTTI 相关的问题,并且能够通过较少的代码量找到它(见下文)。

我有一些TPersistent -后代类TMyObj发布 TArray<Integer> 类型的属性。当我通过 GetDynArrayProp() 收到它的值时并通过 DynArraySize() 查询其大小这只适用于 649 个元素的大小。在此特殊计数之上,将返回一些非常大的值。

请注意,我的数组是从 TDictionary<Integer,Boolean> 的实例生成的的Keys拥有自己的属性(property)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.

最佳答案

简短回答:您的代码已损坏

长答案:

对 getter 的调用正在创建一个新数组(请参阅 TEnumerable<T>.ToArrayImpl 中的 System.Generics.Collections.pas ),该数组在 System.TypInfo.GetDynArrayProp 的尾声中被释放。 (在那里放置一个断点并查看反汇编程序 - 它显示 @DynArrayClear )。由于没有对该数组的其他引用,因此它的内存被释放(如果您进一步进入 System.pas ,您将看到它最终以 _FreeMem 结束)。这意味着对此函数的每次调用都会返回一个悬空指针!

现在为什么您在之前的所有调用中都得到了正确的结果?巧合 - 内存没有被其他任何东西重新分配。

想到了两种不涉及重写 getter 的可能解决方案:

  • 使用 System.Rtti.pas 中的 RTTI如TValue保持引用有效
  • 编写您自己的 GetDynArrayProp 版本使引用保持事件状态 - 但您必须确保始终在之后调用 DynArrayClear,否则会造成内存泄漏

我个人会使用第一个。

关于delphi - DynArraySize() 仅能正确运行最多 649 个整数元素的数组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52482861/

相关文章:

delphi - OpenGL:如何限制图像组件?

delphi - 检索 firebird 用户密码

c++ - 添加大数字 C++ - 初始化程序失败

Java:instanceof vs 类名切换性能

c++ - 标准安全类型检查?

Delphi,TcxGrid 或 TDataSet 中最快的记录访问方法是什么

delphi - Delphi 将图片加载到图像中

有人可以向我解释指向指针的指针吗

c++ - 程序继续运行,没有任何输出

c++ - 在没有 RTTI 的情况下模拟动态转换