Оборачиваем TStringList в запись

Я обычно использую Delphi TStringList для работы с текстом, поэтому я пишу много процедур/функций, таких как:

var
  TempList: TStringList;
begin
  TempList:= TStringList.Create;
  try
    // blah blah blah do stuff with TempList   


  finally
    TempList.Free;
  end;
end;

Было бы неплохо вырезать создание и освобождение для такого общего служебного класса.

Поскольку теперь у нас есть записи с методами, можно ли обернуть класс, такой как TStringList, в запись, чтобы я мог просто иметь:

var
  TempList: TRecordStringList;
begin
  // blah blah blah do stuff with TempList   


end;

person HMcG    schedule 01.08.2009    source источник


Ответы (3)


Возможно. Создайте интерфейс, который предоставляет нужные вам методы/объекты:

type
  IStringList = interface
    procedure Add(const s: string); // etc.
    property StringList: TStringList read GetStringList; // etc.
  end;

Реализуйте интерфейс и оберните его настоящим TStringList:

type
  TStringListImpl = class(TInterfacedObject, IStringList)
  private
    FStringList: TStringList; // create in constructor, destroy in destructor
    // implementation etc.
  end;

Затем реализуйте запись:

type
  TStringListRecord = record
  private
    FImpl: IStringList;
    function GetImpl: IStringList; // creates TStringListImpl if FImpl is nil
                                   // returns value of FImpl otherwise
  public
    procedure Add(const s: string); // forward to GetImpl.Add
    property StringList: TStringList read GetStringList; // forward to
                                                         // GetImpl.StringList
    // etc.
  end;

Тот факт, что внутри записи есть интерфейс, означает, что компилятор будет автоматически обрабатывать подсчет ссылок, вызывая _AddRef и _Release при создании и уничтожении копий, поэтому управление временем жизни выполняется автоматически. Это работает для объектов, которые никогда не будут содержать ссылки на самих себя (создание цикла) — для подсчета ссылок требуются различные приемы, чтобы обойти циклы в графе ссылок.

person Barry Kelly    schedule 01.08.2009
comment
Барри: Будет ли использование этой идеи для всех структур данных в программе способом эффективной обработки сборки мусора? - person lkessler; 01.08.2009
comment
Нет; см. последнее предложение выше о циклах в эталонном графе. Обойти эту проблему может быть очень сложно. - person Mason Wheeler; 01.08.2009
comment
Это круто. Чтобы уточнить, есть ли проблема, только если экземпляр объекта содержит ссылку на свой собственный экземпляр или на любой экземпляр того же класса? - person HMcG; 02.08.2009
comment
Назовем запись здесь R, а экземпляр I. Если есть цепочка ссылок, I -> R -> I -> I -> [...] -> I -> R -> I, где любые два из I экземпляры являются одним и тем же объектом, тогда будет проблема. Но это было бы проблемой только с экземплярами, не говоря уже об обертках записей, поскольку объектам нужна четкая семантика владения - не все, что имеет ссылку на объект, может вызывать Free, только владелец. Подсчет ссылок, реализуемый стратегией оболочки записи, распределяет владение по всем ссылкам, единственным недостатком является то, что циклы позволяют объектам владеть собой. Они не должны использовать обертки. - person Barry Kelly; 02.08.2009
comment
Уже есть интерфейс IStrings, объявленный в StdVCL, и TStringsAdapter в AxCtrls, которые вы можете повторно использовать для этой цели, если хотите, вместо того, чтобы переопределять их как IStringList и TStringListImpl, как показано в примере. - person Ondrej Kelle; 03.08.2009

Если вам посчастливилось выполнить обновление до Delphi 2009, проверьте Работа Барри с интеллектуальными указателями.

TSmartPointer<T: class> = record
strict private
  FValue: T;
  FLifetime: IInterface;
public
  constructor Create(const AValue: T); overload;
  class operator Implicit(const AValue: T): TSmartPointer<T>;
  property Value: T read FValue;
end;

Они действительно классные, но требуют методов Generics и Anonymous. Если вы еще не выполнили обновление до Delphi 2009, сделайте это сейчас! Особенно, когда они предлагают свои специальные предложения BOGO. Вы также получаете Руководство разработчика Delphi бесплатно от Marco всего за загрузка пробной версии. Я тоже уже купил его копию.

person Jim McKeeth    schedule 01.08.2009
comment
Уже модернизирован! Обновился с Turbo Delphi Pro до Delphi 2009 Professional , так что (насколько я понимаю) я получил его в краже. Предложение BOGO было бы вишенкой на торте, но я все равно рассчитываю получить Prism. - person HMcG; 02.08.2009
comment
Хотя я еще даже не прикасался к дженерикам - так многому нужно научиться, так мало времени............ - person HMcG; 02.08.2009

Другой пример уже реализован в CC.

StringList — это то же самое, что и TStringList, за исключением того, что это тип значения. Его не нужно создавать, уничтожать или помещать в try/finally. Это делает компилятор за вас. Для их работы практически нет особых штрафов за производительность:

var 
  strings: StringList;
  astr: string;
begin
  strings.Add('test1');
  strings.Add('test2');
  aStr := string(strings);
  RichEdit.Lines.AddStrings(strings);
end;

Код можно использовать в качестве шаблона для переноса любого TObject в качестве типа класса значения.

В нем уже есть все для раскрытого для вас TStringList.

person Jim McKeeth    schedule 01.08.2009
comment
Спасибо, Джим, я искал в CC и Google, прежде чем публиковать это, и не нашел этого. Есть куча объектов служебного класса, для которых это было бы удобно. Хотя я предпочитаю RStringList ;-) - person HMcG; 02.08.2009
comment
Это не работает для архитектуры x64, поскольку кажется, что InterlockedIncrement/InterlockedDecrement не экспортируются ядром32 (в 64-битной версии). - person flokk; 20.01.2016