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(或者我错过了什么?)
升级到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(或者我错过了什么?)