Подтвердить что ты не робот

Ошибка Delphi XE8 в TList <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.

Этот код печатает "123" в XE7 и раньше (как и должно быть), но в XE8 он печатает "120". Может быть, кто-то знает, что это значит?

Обновление: Неофициальное исправление здесь

4b9b3361

Ответ 1

Я обнаружил, что теперь вызов метода 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]

not

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

Aaargh!

Наконец, я использовал модули System.Generics.Defaults.pas и System.Generics.Collections.pas из Delphi XE7 в своих проектах, и теперь все работает как ожидалось.

Обновление: как я вижу, RTL не затрагивается, так как он не использует TList<T>.Insert для T с SizeOf > 8 (или, может быть, я что-то пропустил?)