В последних версиях Delphi, включая XE8, разработчики столкнулись с рядом проблем, связанных с использованием обобщенных коллекций. Одной из таких проблем является баг в реализации класса TList<T>, который приводит к некорректному поведению при работе с элементами списка. В данной статье мы рассмотрим, как этот баг проявляется, и предложим возможные пути его решения.
Проблема
Проблема заключается в том, что после обновления до версии Delphi XE8, некоторые проекты начинают выдавать неверные данные. Это может быть связано с изменением реализации класса TList<T>. Для демонстрации проблемы рассмотрим следующий пример кода:
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); // здесь должен быть индекс 1, но из-за бага он перезапишет элемент с индексом 0
FRecord.A := 2;
FList.Insert(1, FRecord);
Writeln(IntToStr(FList[0].A) + IntToStr(FList[1].A) + IntToStr(FList[2].A)); // в XE8 выведет "120" вместо ожидаемого "123"
end.
Решение проблемы
Официальное решение от Embarcadero пришло с обновлением XE8 до версии 1, однако разработчикам, столкнувшимся с этой проблемой, не обязательно ждать обновления. Существует неофициальный фикс, который можно найти здесь. Кроме того, был предложен альтернативный путь решения - использование модулей System.Generics.Defaults.pas и System.Generics.Collections.pas из Delphi XE7 в проектах, что позволит избежать проявления бага.
Подробности бага
Проблема заключается в методе TListHelper.InternalInsertN, который зависит от размера данных. В коде метода обнаружены неточности в перемещении данных, что и приводит к ошибке при вставке элементов в список.
procedure TListHelper.InternalInsertN(AIndex: Integer; const Value);
var
ElemSize: Integer;
begin
// ... (проверка индекса)
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);
// ... (дальнейший код)
end;
Рекомендации
Рассмотрите возможность использования неофициального фикса или возврата к использованию модулей из Delphi XE7. Однако, если вы решите восстанавливать реализацию XE7, убедитесь, что RTL, VCL и FMX также используют исправленные модули, чтобы избежать других проявлений этого дефекта.
Заключение
Разработчикам, работающим с обобщенными коллекциями в Delphi XE8, следует быть внимательными к этой проблеме и принять меры для её устранения. Использование неофициального фикса или возвращение к модулям из предыдущей версии может помочь восстановить корректную работу вашего проекта.
Проблема заключается в ошибке в классе `TList` после обновления до Delphi XE8, которая приводит к неправильной вставке элементов в список из-за некорректного перемещения данных.
Комментарии и вопросы
Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.