Обертки для TList
Обертки для TList
Обертки для TList Автор: Dynamic Как известно, в Дельфи для хранения и обработки большого количества однотипных данных используются два основных средства: массивы (статические и динамические) и связные списки. Каждое из них обладает своими достоинствами и недостатками, но их анализ не является целью данной статьи. Наша цель - показать один из способов использования стандартного класса TList в качестве хранилища произвольных данных. В самом простом случае достаточно было бы использования обычного статического или динамического массива, но при их использовании мы неизбежно столкнулись бы с массой неудобств: в первом случае - невозможность динамического изменения размера масссива, во втором - отсутствие автоматического изменения размера массива, и в обоих случаях - отсутствие традиционных средств обработки данных: вставка, удаление, поиск и т.д. TList представляет собой массив указателей, для которого стандартные методы обработки полностью реализованы. Можно сказать, что TList готов к использованию в программах, но он все же обладает одним неудобством: его элементами являются нетипизированные указатели (Pointer), поэтому их приходится каждый раз вручную приводить к нужному типу. Простой пример: PMyType = ^TMyType; TMyType = record Field1, Field2: integer; end; var p: pointer; begin GetMem(p, SizeOf(TMyType)); PMyType(p)^.Field1 := 0; // Перед обращением к Field1 // или // переменную р TMyType(p^).Field2 := 1; // приходится приводить к TMyType ....... Избежать этого позволила бы структура, которая "знает" "свой" тип данных и может выполнять требуемые преобразования автоматически. Мы реализуем ее с помощью класса TList. Одним из достоинств объектно-ориентированного программирования является наследование. Возможность добавлять дочернему классу дополнительную функциональность или изменять существующую без изменения базового позволяет легко создавать целые иерархии классов. Наследуя новый класс от TList, мы сводим нашу задачу к простой модификации его методов в однострочные преобразования параметров из Pointer в TMyType и обратно, например: function TMyTypeList.First: PMyType; begin Result := PMyType(inherited First); end; Именно таким путем пошли разработчики Дельфи, создав группу классов - наследников от TList для удобной работы с обычными объектами (TObjectList), с компонентами (TComponentList) и с целыми классами (TClassList). Поэтому при необходимости хранения массива элементов, порожденных от TObject (а именно этот класс лежит в основании всего дерева классов Дельфи) ничего изобретать уже не придется. Другое дело, если мы имеем дело с простыми структурами, вроде записей (records). Тут уж без создания собственного класса-контейнера не обойтись! Существует еще один способ хранения списка типизированных указателей без использования наследования от TList. Этот способ заключается в создании интерфейсного класса, унаследованного от TObject, с классом TList, включенным в него в качестве приватного поля. В этом случае данные остаются на хранении у TList, а в новый класс включаются только необходимые методы доступа к ним, делегирующие свои обязанности соответствующим методам того же TList. Небольшой пример такого класса: type PMyType = ^TMyType; TMyType = record Field1, Field2: integer; end; TMyTypeList = class private FList: TList; // хранилище данных public constructor Create; destructor Destroy; override; function AddItem(Item: TMyType): Integer; procedure RemoveItem(ItemIndex: Integer); function ObjOf(Idx: integer): TMyType; function PtrOf(Idx: integer): PMyType; function Count: integer; procedure Exchange(idx1, idx2: integer); end; ........................ implementation ........................ function TMyTypeList.AddItem(Item: TMyType): Integer; var p: PMyType; begin GetMem(p, SizeOf(TMyType)); // выделением памяти занимаемся самостоятельно Move(Item, p^, SizeOf(TMyType)); Result := FList.Add(p); // просто перенаправляем вызов end; function TMyTypeList.Count: integer; begin Result := FList.Count; // просто перенаправляем вызов end; constructor TMyTypeList.Create; begin inherited Create; FList := TList.Create; // не забываем создать объект end; destructor TMyTypeList.Destroy; begin FList.Free; // уничтожаем объект inherited Destroy; end; procedure TMyTypeList.Exchange(idx1, idx2: integer); begin FList.Exchange(idx1, idx2); // просто перенаправляем вызов end; function TMyTypeList.ObjOf(Idx: integer): TMyType; begin FillChar(Result, SizeOf(TMyType), #0); Result := TMyType(FList.Items[Idx]^); // перенаправляем вызов и приводим к TMyType end; function TMyTypeList.PtrOf(Idx: integer): PMyType; begin Result := FList.Items[Idx]; // просто перенаправляем вызов end; procedure TMyTypeList.RemoveItem(ItemIndex: Integer); begin FreeMem(FList[ItemIndex]); // освобождаем память тоже сами FList.Delete(ItemIndex); end; В дальнейшем работа с данными не представляет проблем и выглядит примерно так: var MyList: TMyTypeList; ........... MyList.ObjOf(0).Field1 := 2005;