общий TList записей с подсписком?

Я хочу использовать общий TList записей с подсписком в Delphi XE5:

type
  TMyRecord=record
    Value1: Real;
    SubList: TList<Integer>;
  end;

  TMyListOfRecords=TList<TMyRecord>;

var
  MyListOfRecords: TMyListOfRecords;

Невозможны присвоения поля записей:

MyListOfRecords[0].Value1:=2.24; 

or

MyListOfRecords[0].SubList:=TList<Integer>.Create;

приведет к ошибке "левая сторона не может быть назначена" компилятором.

См. также: Как изменить значение записи TList‹record›?

Работает следующий обходной путь:

AMyRecord:=MyListOfRecords[0];
AMyRecord.Value1:=2.24;
AMyRecord.SubList:=TList<Integer>.Create;
AMyRecord.SubList.Add(33);
MyListOfRecords[0]:=AMyRecord;

Из-за проблем с производительностью я хотел бы избежать копирования данных во временную запись AMyrecord. Я бы предпочел получить прямой доступ к полям записи и подсписку.

Каков наилучший способ справиться с этим?


person user3384674    schedule 06.03.2014    source источник


Ответы (1)


Список предоставляет свое внутреннее хранилище, которое представляет собой динамический массив, через List собственность. Итак, вы можете написать:

MyListOfRecords.List[0].Value1 := 2.24; 

Влияет ли это на какую-либо измеримую разницу в производительности по сравнению с альтернативой с копиями значений, я не могу сказать. Стоило бы это проверить.

Как правильно говорит @LURD, List возвращает внутреннюю память. И это может иметь более Count элементов. В частности, он имеет Capacity элементов. Итак, если вы используете его, вы должны получить доступ к элементам, используя индексацию массива, по элементам с 0 по Count-1. Помните также, что изменение размера списка может привести к перераспределению и, следовательно, к перемещению внутренней памяти. Любая ссылка на List действительна только до следующего перераспределения.

Эти предупреждения должны подсказать вам, что вы можете использовать List только в том случае, если этого требуют ограничения производительности. И даже в этом случае используйте его экономно.

В моей кодовой базе есть альтернатива TList<T>, чье свойство Items[] возвращает указатель на элемент. Контейнер по-прежнему хранится как динамический массив для эффективного распределения памяти. Я предпочел эту опцию свойству List, потому что чувствовал, что это приводит к более чистому коду.


Хорошо, вы просили взглянуть на мой класс списка, который возвращает указатели на элементы. Вот:

type
  TReferenceList<T> = class(TBaseValueList<T>)
  type
    P = ^T;
  private
    function GetItem(Index: Integer): P;
  public
    property Items[Index: Integer]: P read GetItem; default;
  public
    // .... helper types for enumerators excised
  public
    function GetEnumerator: TEnumerator;
    function Enumerator(Forwards: Boolean): TEnumeratorFactory;
    function ReverseEnumerator: TEnumeratorFactory;
    function IndexedEnumerator: TIndexedEnumeratorFactory;
  end;

Нужны пояснения. Базовый класс, TBaseValueList<T>, является моей альтернативой TList<T>. Вы можете заменить TList<T>, если хотите. Нет, потому что в моем базовом классе нет свойства Items. Это потому, что я хочу, чтобы специализированные классы представили его. Другая моя специализация:

type
  TValueList<T> = class(TBaseValueList<T>)
  private
    function GetItem(Index: Integer): T;
    procedure SetItem(Index: Integer; const Value: T);
  public
    property Items[Index: Integer]: T read GetItem write SetItem; default;
  end;

Реализация моего TBaseValueList<T> довольно очевидна. Очень похоже на TList<T>. Я не думаю, что вам действительно нужно видеть какую-либо реализацию. Это все очень очевидно.

В качестве простого способа получить ссылку на элемент вы можете обернуть List следующим образом:

type
  TMyList<T> = class(TList<T>)
  public
    type
      P = ^T;
  private
    function GetRef(Index: Integer): P;
  public
    property Ref[Index: Integer]: P read GetRef;
  end;

function TMyList<T>.GetRef(Index: Integer): P;
begin
  Result := @List[Index];
end;

Если вам нужен более богатый набор контейнеров, чем в Delphi, вы можете обратить внимание на Spring4D. Хотя я не уверен, есть ли у них что-то вроде моего контейнера, который возвращает ссылки.

person David Heffernan    schedule 06.03.2014
comment
Обратите внимание, что динамический массив свойства List может быть больше, чем MyListOfRecords.Count. Так что не передавайте его подпрограммам, перебирающим все элементы. - person LU RD; 06.03.2014
comment
@LURD Спасибо. FWIW, я думаю, что List prop действительно является ответом на вопрос, который был задан, заметьте. Это единственный способ получить прямой доступ к элементам с vanilla TList<T>. - person David Heffernan; 06.03.2014
comment
@ Дэвид Хеффернан Спасибо! Можно ли увидеть вашу альтернативу TList‹T› и как вы ее используете? После многого поиска по этой теме я думаю, что это может помочь многим людям. - person user3384674; 06.03.2014
comment
@ user3384674 Это почти то же самое, что и TList<T>. У него нет свойства Items. Это главное отличие. Вы можете взять код из TList<T> и удалить Items, GetItem и SetItem, и вы будете довольно близки. Счетчики тоже довольно крутые. Я не особо хочу показывать весь код в компилируемом виде. Я не уверен, что миру нужен еще один набор контейнеров. Вы должны быть в состоянии сами следовать этим идеям из того, что я вам дал. Надеюсь. Вы также должны дважды проверить, является ли производительность проблемой. - person David Heffernan; 06.03.2014
comment
Вы даже можете создать подкласс TList<T> и добавить свойство, которое возвращает ссылку на элемент, используя свойство List, которое я описал. Это был бы простой способ сделать это. Или даже сделайте это с помощником класса. - person David Heffernan; 06.03.2014
comment
@user3384674 user3384674 Я сделал еще одно редактирование, чтобы показать простой способ сделать доступ к ссылкам более чистым. - person David Heffernan; 06.03.2014
comment
@ Дэвид Хеффернан Прежде всего, я хотел бы сказать, что я впечатлен всеми вашими усилиями, которые вы приложили, отвечая на вопрос. Большое Вам спасибо. Чего я не понимаю: в чем разница между MyListOfRecords.List[0].Value1 := 2.24; и если я укажу ваш последний TMyList с MyListOfRecords.Ref[0]^.Value1 := 2.24; ? - person user3384674; 06.03.2014
comment
Нет никакой разницы. Не стесняйтесь использовать первый! - person David Heffernan; 06.03.2014