Delphi TList<T> 中的 XE8 错误,需要解决方法

Delphi XE8 bug in TList<T>, need workaround

升级到XE8后,我们的一些项目开始出现数据中断。看起来像是 TList 实现中的错误。

program XE8Bug1;
{$APPTYPE CONSOLE}

uses
  System.SysUtils, Generics.Collections;

type
  TRecord = record
    A: Integer;
    B: Int64;
  end;

var
  FRecord: TRecord;
  FList: TList<TRecord>;

begin
  FList := TList<TRecord>.Create;
  FRecord.A := 1;
  FList.Insert(0, FRecord);
  FRecord.A := 3;
  FList.Insert(1, FRecord);
  FRecord.A := 2;
  FList.Insert(1, FRecord);
  Writeln(IntToStr(FList[0].A) + IntToStr(FList[1].A) + IntToStr(FList[2].A));

end.

此代码在 XE7 及之前的版本中打印“123”(应该如此),但在 XE8 中它打印“120”。 也许有人知道这个的快速修复方法?

更新: 非官方修复是 here

我发现现在 TList<T>.Insert 方法调用 TListHelper.InternalInsertX 取决于数据大小,在我的例子中:

procedure TListHelper.InternalInsertN(AIndex: Integer; const Value);
var
  ElemSize: Integer;
begin
  CheckInsertRange(AIndex);

  InternalGrowCheck(FCount + 1);
  ElemSize := ElSize;
  if AIndex <> FCount then
    Move(PByte(FItems^)[AIndex * ElemSize], PByte(FItems^)[(AIndex * ElemSize) + 1], (FCount - AIndex) * ElemSize);
  Move(Value, PByte(FItems^)[AIndex * ElemSize], ElemSize);
  Inc(FCount);
  FNotify(Value, cnAdded);
end;

我在第一个 Move 电话中看到了问题。目的地应该是:

PByte(FItems^)[(AIndex + 1) * ElemSize]

不是

PByte(FItems^)[(AIndex * ElemSize) + 1]

啊啊啊!

最后,我在我的项目中使用了 Delphi XE7 的 System.Generics.Defaults.pas 和 System.Generics.Collections.pas 单元,现在一切正常。

更新:如我所见,RTL 不受影响,因为它不使用 TList<T>.Insert 用于 SizeOf > 8 的 T(或者我错过了什么?)