问题
After upgrading to XE8 some of our projects start to break data. Looks like a bug in TList realization.
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.
This code prints "123" in XE7 and before (as it should be), but in XE8 it prints "120". Maybe someone know a quickfix for this?
Update: unofficial fix is here
回答1:
I found that now the TList<T>.Insert
method call TListHelper.InternalInsertX
depends on the data size, in my case:
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;
I see the problem in the first Move
call. Destination should be:
PByte(FItems^)[(AIndex + 1) * ElemSize]
not
PByte(FItems^)[(AIndex * ElemSize) + 1]
Aaargh!
Finally, I've used the System.Generics.Defaults.pas and System.Generics.Collections.pas units from Delphi XE7 in my projects, and now all works as expected.
Update: as I see, RTL not affected, as it isn't use TList<T>.Insert
for T with SizeOf > 8 (or maybe I miss something?)
来源:https://stackoverflow.com/questions/29906723/delphi-xe8-bug-in-tlistt-need-workaround