38

升级到 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”。也许有人知道这个的快速修复?

更新:非官方修复在这里

4

1 回答 1

32

我发现现在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 (或者我错过了什么?)

于 2015-04-27T22:45:05.133 回答