Глава 7
Списки и коллекции
В процессе разработки программного кода приложения часто бывает необходимо хранить наборы различных переменных или объектов. Кроме этого, некоторые компоненты VCL (тСотbоВох, TListBox, TDBComboBox, ТМето, TStringGrid) используют для хранения информации списки строк.
В принципе хранение и использование наборов значений можно обеспечить при помощи хорошо всем известных массивов. Однако их прямое использование требует от разработчика дополнительных усилий. Ведь при реализации программной логики необходимо добавлять в массив новые элементы, изменять существующие и удалять ненужные. Кроме этого, часто бывает необходимо найти элемент массива по значению. Все эти операции стандартны и повторяются для списков любых типов данных.
Для решения перечисленных задач в Delphi доступны для использования специальные классы. Помимо хранения списков значений в них реализованы свойства, позволяющие контролировать состояние списка и методы, обеспечивающие редактирование списка и поиск в нем отдельных элементов.
Для работы со строковыми списками предназначены классы TStrings и TStringList.
Любые типы данных можно заносить в список указателей, который реализован в классе TList.
Использование наборов объектов (широко применяются в классах VCL), которые называются коллекциями, осуществляется при помощи классов TCollection И TCollectionItem.
В этой главе рассматриваются следующие вопросы.
Список строк
Строковый тип данных широко используется программистами. Во-первых, многие данные действительно необходимо представлять при помощи этого типа. Во-вторых, множество функций преобразования типов позволяют представлять числовые типы в виде строк, избегая тем самым проблем с несовместимостью типов.
По этой причине в первую очередь мы займемся изучением списка строк, который инкапсулирован в классах TStrings и TStringList. Первый класс является абстрактным и служит платформой для создания реально работающих потомков. Второй класс реализует вполне работоспособный список строк. Рассмотрим эти классы подробнее.
Класс TStrings
Класс TStrings является базовым абстрактным классом, который обеспечивает потомков основными свойствами и методами, позволяющими создавать работоспособные списки строк. Его прямым предком является класс TPersistent.
Основой, вокруг которой строится вся логика списка, является свойство, обеспечивающее хранение элементов списка:
property Strings[Index: Integer]: string;
Обращение к отдельному элементу списка может осуществляться через это свойство
SomeStrings.Strings[i] := Editl.Text;
или
SomeStrings[i] := Editl.Text;
Оба способа равноценны.
При помощи простого присваивания, описанного выше, можно задавать новые значения только тогда, когда элемент уже создан. Для добавления элемента используются методы Add и AddString, подробно описанные в следующем разделе.
С каждым элементом списка можно связать любой объект. Для этого используется свойство
property Objects[Index: Integer]: TObject;
Свойство Strings элемента и свойство objects связанного с ним объекта имеют одинаковые индексы. Если строка не имеет связанного объекта, то свойство objects равно Nil. Один объект может быть связан с несколькими строками списка одновременно.
Чаще всего объекты нужны для того, чтобы хранить для каждого элемента дополнительную информацию. Например, в списке городов для каждого элемента можно дополнительно хранить население, площадь, административный статус и т. д. Для этого можно создать примерно такой класс:
TSityProps = class(TObject) Square: Longint;
Population: Longint;
Status: Strings; end;
Для того чтобы добавить к строке из списка объект, используется метод
AddObject:
function AddObject(const S: string; AObject: TObject): Integer; virtual;
Обратите внимание, что в параметре AObject необходимо передавать указатель на объект. Проще всего это сделать так:
SomeStrings.AddObject('Someltem', TSityProps.Create);
Вспомогательные свойства класса обеспечивают разработчика информацией о состоянии списка. Дополнительные методы осуществляют поиск в списке и взаимодействие с файлами и потоками.
Класс TStrings является абстрактным и большинство его методов перекрыты в потомке TStringList, а реального механизма заполнения списка не существует вообще. Поэтому при создании программной логики программист использует класс TStringList, и подробное описание всех свойств и методов списка строк будет представлено ниже.
Попытка прямого использования в приложении класса TStrings вызовет ошибку использования абстрактного класса на этапе выполнения программы, а именно при попытке заполнить список значениями. Простая замена типа объектной переменной списка на TStringList делает приложение полностью работоспособным без какого-либо дополнительного изменения исходного кода.
Класс TStringList
Класс TStringList обеспечивает реальное использование списков строк в приложении. В нем перекрыты основные свойства и методы предка — класса TStrings (см. выше).
По существу, класс представляет собой оболочку вокруг динамического массива значений списка, представленного свойством strings. Объявление свойства (унаследованное от TStrings) выглядит так:
property Strings[Index: Integer]: string read Get write Put; default;
Для работы со свойством используются внутренние методы Get и put, в которых используется внутренняя переменная FList:
PStringItem = ^TStringItem;
TStringItem = record FString: string;
FObj ect: TObject;
end;
PStringItemList = ^TStringItemList;
TStringItemList = array[0..MaxListSize] of TStringItem;
FList: PStringItemList;
Из ее объявления видно, что список строк представляет собой динамический массив записей TStringitem. Эта запись позволяет объединить саму строку и связанный с ней объект.
Другие свойства и методы класса представлены в табл. 7.1. Они обеспечивают выполнение разнообразных функций, существенно облегчающих работу программиста со списком.
Таблица 7.1. Свойства и методы класса TStringLlst
Объявление |
Описание |
||
Property Capacity: Integer; |
Определяет число строк, для которых выделена память |
||
Property CommaText: string; |
Содержит представление строк списка в формате SDF (System Data Format). Значения в нем объединяются в одну строку, берутся в двойные кавычки и разделяются запятой или пробелом |
||
Property Count: Integer; |
Возвращает число строк в списке |
||
Type TDuplicates = (duplgnore, dupAccept, dupError) ; |
Задает реакцию списка на попытку внести повторяющиеся строки. |
||
Property Duplicates: Tduplicates;
|
Duplgnore — ввод повторяющихся строк игнорируется DupAccept — вводить повторяющиеся строки раз решается DupError — при вводе повторяющихся строк генерируется исключительная ситуация EstringListError |
Property Names[Index: Integer]: string; |
Если строка содержит выражение в виде равенства, свойство возвращает ту его часть, которая расположена до знака равно "=" |
||
Property Objects[Index: Integer]: TObject; |
Содержит список объектов, связанных со строками списка |
||
Property Sorted: Boolean; |
При значении True сортирует строки списка |
||
Property Strings[Index: Integer]: string; default; |
Указывает на список строк |
||
Property StringsAdapter: IstringsAdapter; |
СОМ — интерфейс Istrings |
||
Property Text: strings; |
Возвращает список строк в виде одной строки, где элементы разделены символами возврата каретки и переноса строки |
||
property Values[const Name: string]: strings; |
Если строка содержит выражение в виде равенства, свойство возвращает ту его часть, которая расположена после знака равно "=". В параметре передается левая часть равенства |
||
function Add(const S: string): Integers-override; |
Добавляет к списку новую строку. Возвращает номер нового элемента |
||
function AddObject(const S: string; AObject: TObject): Integers-virtual; |
Добавляет к списку новую строку s и связывает с ней объект AObjecl. Возвращает номер нового элемента в списке |
||
procedure AddStrings(Strings: TStrings); virtual; |
Добавляет к списку список Strings |
||
procedure Append(const S: string) ; |
Добавляет к списку новую строку |
||
procedure Assign(Source: TPersistent); override; |
Переносит список Source (строки и объекты) в данный список |
||
procedure BeginUpdate; |
Вызывается перед внесением изменений в список |
||
procedure Clear; override; |
Удаляет из списка все строки |
||
procedure Delete(Index: Integer); override; |
Удаляет строку с индексом index |
||
procedure EndUpdate; |
Вызывается после внесения изменений в список |
procedure Exchange(Indexl, Index2: Integer); override; |
Меняет местами строки с индексами Index1 и Index2 |
||
function Equals(Strings: TStrings): Boolean; |
Сравнивает список Strings сданным. Возвращает True, если списки совпадают |
||
Function Find(const S: string; var Index: Integer): Boolean; virtual; |
Осуществляет поиск строки в отсортированном списке, заданной параметром S. В случае успеха возвращает True. В параметре index возвращается порядковый номер найденного элемента |
||
Function GetText: Pchar; virtual; |
Возвращает значение свойства Text в текстовом буфере |
||
Function IndexOf(const S: string): Integer-override; |
Возвращает индекс строки S |
||
function IndexOfName(const Name: string): Integer; |
Возвращает индекс строки, заданной левой частью равенства (см. свойства Name и Values) |
||
function IndexOfObj ect(AObj ect: TObject) : Integer; |
Возвращает индекс первой строки в списке, с которой связан объект, передаваемый в параметре AObject |
||
procedure Insert(Index: Integer; const S: string); override; |
Вставляет новую строку s в позицию с индексом Index |
||
procedure InsertObj ect(Index: Integer; const S: string; AObject: TObject) ; |
Вставляет новую строку s в позицию с индексом Index. Связываете ней объект AObject |
||
procedure LoadFromFile(const FileName: strings-virtual; |
Загружает файл FileName в список. Каждая строка файла становится строкой списка |
||
procedure LoadFromStream(Stream: TStream); virtual; |
Загружает поток Stream в список. Каждая строка потока становится строкой списка |
||
procedure Move(Curlpdex, Newlndex: Integers-virtual; |
Переносит строку с индексом Curlndex в позицию с индексом Newlndex |
procedure SaveToFile(const FileName: string); virtual; |
Сохраняет список в файле FileName. Каждая строка записывается в файл с новой строки |
procedure SaveToStream(Stream: TStream); virtual; |
Сохраняет список в потоке Stream. Каждая строка записывается в файл с новой строки |
||
procedure SetText(Text: PChar); virtual;
|
Задает значение свойства Text. Отдельные записи в параметре Text должны быть разделены символами возврата каретки и переноса строки |
||
procedure Sort; virtual; |
Сортирует строки списка |
||
property OnChange: TNotifyEvent; |
Вызывается сразу после изменения списка |
||
property OnChanging: TNotifyEvent; |
Вызывается перед изменением списка |
Помимо свойства strings, содержимое списка можно получить при помощи свойств Text и conimaText. Они представляют все строки списка в виде одной длинной строки. При этом в первом свойстве элементы списка разделены символами возврата каретки и переноса строки. Во втором свойстве строки заключены в двойные кавычки и разделены запятыми или пробелами. Так, для списка городов (Москва, Петербург, Одесса) свойство Text будет равно
Москва#$0#$АПетербург#$0#$АОдесса
А свойство CommaText равно
"Москва", "Петербург", "Одесса"
Важно иметь в виду, что эти свойства доступны не только для чтения, но и для записи. Так что заполнить ваш TstringList, вы сможете не только циклически вызывая Add или insert, но и одним-единственным присвоением значения Text или CommaText.
Довольно часто в списках размещается строковая информация следующего вида:
'Name=Value'
В качестве примера можно привести строки из файлов ini или системного реестра. Специально для таких случаев в списке предусмотрено представление строк в двух свойствах. В свойстве Names содержится текст до знака равенства. В свойстве Values содержится текст после знака равенства. Доступ к значениям свойства values осуществляется по значению. Например, если в списке есть строка
City=Petersburg
То значение свойства value будет равно
Value['City'] - 'Petersburg'
Так как основу списка составляет динамический массив, то для него в процессе работы должна выделяться память. Свойство Capacity позволяет определить, сколько строк помещается в отведенную память. При добавлении в список новой строки память для нее выделяется автоматически. Однако вы можете изменять выделяемый объем самостоятельно при помощи того же свойства capacity.
Однако при этом необходимо следить, чтобы выделенной памяти хватало на уже существующие строки. Для этого значение свойства Capacity должно быть всегда больше или равно значению свойства count, содержащего число строк в списке.
Добавление новых строк осуществляется при помощи 'методов Add и insert. При отсутствии сортировки (sorted=False) первый метод добавляет строку в конец списка, а второй вставляет ее в указанное место. Если список отсортирован, пользоваться методом insert не следует: все равно строка будет вставлена в соответствии с сортировкой.
Также в список можно добавить и сразу несколько строк. Для этого применяются методы AddStrings и Assign. Причем второй метод вместе со строками добавляет и связанные с ними объекты.
Для добавления одного объекта можно использовать метод AddObject. Обратите внимание, что добавить объект при помощи этого метода можно только вместе с новой строкой (см. раздел "Класс TStrings"). Для того чтобы добавить объект к уже существующей строке, необходимо сначала создать объект, а затем присвоить его свойству objects:
var SPb: TSityProps;
SPb := TSityProps.Create; (Создание объекта} SPb.Population := 5000000;
SomeStrings.Strings[i] := 'Санкт-Петербург';
SomeStrings.Objects[i] := SPb; (Связывание объекта и строки)
Также можно поступить следующим образом (помните, что строка уже должна существовать):
SomeStrings.Strings[i] := 'Санкт-Петербург';
SomeStrings.Objects[i] := TSityProps.Create ;
(SomeStrings.Objects[i] as TSityProps).Population := 5000000;
Очень важно иметь в виду, что добавляемые в список объекты не уничтожаются вместе с ним; перед вызовом деструктора списка обязательно уничтожьте входящие в него объекты.
При помощи метода Exchange можно поменять местами две строки, а метол Move позволяет перенести одну запись на новое место.
Большое значение имеет операция поиска необходимой строки. Для этого создан ряд методов, осуществляющих поиск по разным параметрам:
Все они возвращают индекс найденной строки. Если строка не найдена, возвращается значение -1. Метод Find также осуществляет поиск строки в отсортированном списке, но возвращает True или False.
Класс TStringList невозможно представить себе без возможностей сортировки. Если вас удовлетворит обычная сортировка, то для этого можно использовать свойство sorted (сортировка выполняется при значении True) или метод sort. Под "обычной" имеется в виду сортировка по тексту строк с использованием функции AnsiCompareStr (то есть с учетом национальных символов, в порядке возрастания). Если вы хотите отсортировать список по другому критерию, к вашим услугам метод:
type TStringListSortCompare = function(List: TStringList;
Indexl, Index2: Integer): Integer-procedure CustomSort(Compare: TStringListSortCompare); virtual;
Чтобы отсортировать список, вы должны описать функцию сравнения двух элементов с индексами Indexl и Index2, которая должна возвращать следующие результаты:
Для описанного выше примера с объектом-городом нужны три процедуры:
function SortByStatus(List: TStringList;
Indexl, Index2: Integer): Integer;
begin
Result := AnsiCompareStr( (List.Objects[Indexl] as TCityProps).Status, (List.Objects[Index2] as TCityProps).Status;
end;
function SortBySquare(List: TStringList; Indexl,Index2: Integer):Integer;
begin
if (List.Objects[Indexl] as TCityProps).Square < (List.Objects[Index21 as TCityProps). Square) then Result := -1 else if (List.Objects[Indexl] as TCityProps)-Square =
(List.Objects[Index2] as TCityProps).Square then Result := 0 else Result := 1;
end;
function SortByPopulation(List: TStringList;
Indexl, Index2: Integer):Integer;
begin
if (List.Objects[Indexl] as TCityProps).Population <
(List.Objects[Index2] as TCityProps). Population then Result :=-1 else if (List.Objects[Indexl] as TCityProps). Population =
(List.Objects[Index2] as TCityProps). Population then Result :=0 else Result := 1;
end;
Далее передаем одну из них в метод CustomSort:
Cities.CustomSort( SortByPopulation );
Список можно загрузить из файла или потока. Для этого используются методы LoadFromFile И LoadFromStream. Сохранение списка выполняется методами SaveToFile и SaveToStream.
Перед изменением списка вы можете получить управление, описав обработчик события OnChanging, а после изменения — OnChange.
Список указателей
Для хранения списка указателей на размещенные в адресном пространстве структуры (объекты, динамические массивы, переменные) предназначен класс TList. Так же как и список строк, список указателей обеспечивает эффективную работу с элементами списка.
Класс TList
Основой класса TList является список указателей. Сам список представляет собой динамический массив указателей, к которому можно обратиться через индексированное свойство
property Items [Index: Integer]: Pointer;
Нумерация элементов начинается с нуля. Прямой доступ к элементам массива возможен через свойство
type
TPointerList = array[0..MaxListSize-1] of Pointer;
PPointerList = ^TPointerList;
property List: PPointerList;
которое имеет атрибут "только для чтения".
Так как элементы списка являются указателями на Некоторые структуры, прямое обращение к составным частям этих структур через свойство items невозможно. Как это можно сделать, рассказывается в нижеприведенном примере.
В списке могут содержаться указатели на разнородные структуры. Не обязательно хранить в списке только указатели на объекты или указатели на записи.
Реализованные в классе TList операции со списком обеспечивают потребности разработчика и совпадают с операциями списка строк.
Для добавления к концу списка нового указателя используется метод function Add(Item: Pointer): Integer;
Прямое присваивание значения элементу, который еще не создан при помощи метода Add, вызовет ошибку времени выполнения.
Новый указатель можно добавить в нужное место списка. Для этого используется метод
procedure Insert(Index: Integer; Item: Pointer);
В параметре index указывается нужный порядковый номер в списке. Перенос существующего элемента на новое место осуществляется методом
procedure Move(Curlndex, Newlndex: Integer);
Параметр Curlndex определяет старое положение указателя. Параметр Newlndex задает новое его положение.
Также можно поменять местами два элемента, определяемые параметрами Indexl и Index2:
procedure Exchange(Indexl, Index2: Integer);
Для удаления указателей из списка используются два метода. Если известен индекс, используется метод
procedure Delete(Index: Integer);
Если известен сам указатель, используется метод
function Remove(Item: Pointer): Integer;
Эти методы не уменьшают объем памяти, выделенной под список. При необходимости сделать это необходимо использовать свойство capacity. Для увеличения объема отводимой памяти применяют метод Expand, который производит увеличение автоматически в зависимости от текущего размера списка.
function Expand: TList;
Для того чтобы метод "сработал", необходимо, чтобы Count = Capacity-Алгоритм работы метода представлен в табл. 7.2.
Таблица 7.2. Алгоритм увеличения памяти списка
Старое значение свойства Capacity |
Новое значение свойства Capacity |
<4 |
4 |
4...8 |
8 |
>8 |
16 |
Метод
procedure Clear; dynamic;
используется для удаления всех элементов списка сразу. Для поиска указателя по его значению используется метод
function IndexOffItem: Pointer): Integer;
Метод возвращает индекс найденного элемента в списке. При неудачном поиске возвращается -1.
Для сортировки элементов списка применяется метод
type TListSortCompare = function (Iteml, Item2: Pointer): Integer; procedure Sort(Compare: TListSortCompare);
Так как состав структуры, на которую указывает элемент списка, невозможно заранее обобщить, разработка процедуры, осуществляющей сортировку, возлагается на программиста. Метод Sort лишь обеспечивает сравнение попарно указателей на основе созданного программистом алгоритма.
Полностью все свойства и методы класса TList представлены в табл. 7.3.
Таблица 7.3. Свойства и методы класса TList
Объявление |
Описание |
property Capacity: Integer; |
Определяет число строк, для которых выделена память |
property Count: Integer; |
Возвращает число строк в списке |
property Items[Index: Integer]: Pointer; |
Список указателей |
Type TPointerList -array[0..MaxListSize-1] of Pointer; PPointerList = ^TPointerList; property List: PPointerList; |
Динамический массив указателей
|
function Add(Item: Pointer): Integer; |
Добавляет к списку новый указатель |
procedure Clear; dynamic/ |
Полностью очищает список |
procedure Delete(Index: Integer); |
Удаляет указатель с индексом Index |
class procedure Error(const Msg: string; Data: Integers-virtual; |
Генерирует исключительную ситуацию EListError. Сообщение об ошибке создается из форматирующей строки Msg и числового параметра Data |
procedure Exchange(Index1, Index2: Integer); |
Меняет местами указатели с индексами Indexl и Index2 |
function Expand: TList; |
Увеличивает размер памяти, отведенной под список |
function First: Pointer; |
Возвращает первый указатель из списка |
function IndexOf(Item: Pointer): Integer; |
Возвращает индекс указателя, заданного параметром Item |
procedure Insert(Index: Integer; Item: Pointer); |
Вставляет новый элемент Item в позицию Index |
function Last: Pointer; |
Возвращает последний указатель в списке |
procedure Move(Curlndex, Newlndex: Integers; |
Перемещает элемент списка на новое место |
procedure Pack; |
Удаляет из списка все пустые (Nil) указатели |
function Remove(Item: Pointer): Integer; |
Удаляет из списка указатель Item |
type TlistSortCompare = function (Iteml/ Item2: Pointer): Integer; procedure Sort(Compare: TListSortCompare); |
Сортирует элементы списка
|
Пример использования списка указателей
Рассмотрим использование списков указателей на примере приложения DemoList. При щелчке мышью в форме приложения отображается точка, которой присваивается порядковый номер. Одновременно координаты и номер точки записываются в соответствующие свойства создаваемого экземпляра класса TMypixel. Указатель на этот объект передается в новый элемент списка pixList.
В результате, после очистки формы всю последовательность точек можно восстановить, используя указатели на объекты точек из списка.
Список точек можно отсортировать по координате Х в порядке возрастания.
Листинг 7.1 Модуль главной формы проекта DemoList
unit Main;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCLrls, Buttons;
type
TMainForm = class(TForm)
ListBtn: TBitBtn;
ClearBtn: TBitBtn;
DelBtn: TBitBtn;
SortBtn: TBitBtn;
procedure FormCreate(Sender: TObject);
procedure FotmClose(Sender: TObject; var Action: TCloseAction);
procedure FormMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integers); procedure ListBtnClick(Sender: TObject);
procedure ClearBtnClick(Sender: TObject);
procedure DelBtnClick(Sender: TObject);
procedure SortBtnClick(Sender: TObject);
private
PixList: TList;
PixNum: Integer;
public
{ Public declarations } end;
TMyPixel = class(TObject) FX: Integer;
FY: Integer;
FText: Integer; constructor Create(X, Y, Num: Integer) ;
procedure SetPixel;
end;
var
MainForm: TMainForm;
irnplementation
{$R *.DFM}
const PixColor = cIRed;
var CurPixel: TMyPixel;
constructor TMyPixel.Create(X, Y, Num: Integers); begin
inherited Create;
FX := X;
FY := Y;
FText := Num;
SetPixel;
end;
procedure TPixel.SetPixel ;
begin MainForm.Canvas.PolyLine([Point(FX, FY), Point(FX, FY)]) ;
MainForm. Canvas. TextOut (FX +1, FY + 1, IntToStr (FText) );
end;
function PixCompare (Iteml, Iteirt2: Pointer): Integer;
var Pixl, Pix2: TMyPixel;
begin
Pixl := Iteml;
Pix2 := Item2;
Result := Pixl.FX - Pix2. EX;
end;
procedure TMainForm.FormCreate(Sender: TObject);
begin
PixList := TList.Create;
PixNum := 1; {Счетчик точек)
Canvas.Pen.Color := PixColor; (Цвет точки}
Canvas.Pen.Width := 3; (Размер точки)
Canvas.Brush.Color := Color; (Цвет фора текста равен цвету формы} end;
procedure TMainForm.FormClose(Sender: TObject; var Action: TCloseAction);
begin PixList.Free;
end;
procedure TMainForm.FormMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
PixList.Add(TPixel.Create(X, Y, PixNum));
Inc(PixNum);
end;
procedure TMainForm.ListBtnClick(Sender: TObject);
var i: Integer;
begin
with PixList do
for i := 0 to Count - 1 do
begin
CurPixel := Items[i];
CurPixel.SetPixel ;
end;
end;
procedure TMainForm.ClearBtnClick(Sender: TObject);
begin
Canvas.FillRect(Rect(0, 0, Width, Heights)); end;
procedure TMainForm.DelBtnClick(Sender: TObject);
begin
PixList.Clear;
PixNuin := 1;
end;
procedure TMainForm.SortBtnClick(Sender: TObject);
var i: Integer;
begin
PixList.Sort(PixCompare);
with PixList do
for i := 0 to Count - 1 do TMyPixel(Items[i]).FText := i + 1;
end;
end.
Класс TMypixel обеспечивает хранение координат точки и ее порядковый номер в серии. Эти параметры передаются в конструктор класса. Метод setpixel обеспечивает отрисовку точки на канве формы (см. главу 8).
Экземпляр класса создается для каждой новой точки в методе-обработчике FormMouseDown при щелчке кнопкой мыши. Здесь же указатель на новый объект сохраняется в элементе списка PixList, создаваемом при помощи метода Add. Таким образом, программа "запоминает" расположение и порядок следования точек.
Метод-обработчик ListBtnciick обеспечивает отображение точек. Для этого в цикле текущий указатель списка передается в переменную объектного типа curpixel. To есть в этой переменной по очереди "побывают" все созданные объекты, указатели на которые хранятся в списке.
Это сделано для того, чтобы получить доступ к свойствам объектов (непосредственно через указатель этого сделать нельзя). Другой способ приведения типа использован в методе-обработчике SortBtnciick.
Перед вторичным отображением точек необходимо очистить поверхность формы. Эту операцию выполняет метод-обработчик clearBtnciick.
Список точек можно отсортировать по координате Х в порядке возрастания. Для этого в методе-обработчике SortBtnclick вызывается метод Sort списка PixList. В параметре метода (переменная процедурного типа) передается функция Pixcompare, которая обеспечивает инкапсулированный в методе sort механизм перебора элементов списка алгоритмом принятия решения о старшинстве двух соседних элементов.
Если функция возвращает положительное число, то элемент iteml больше элемента item2. Если результат отрицательный, то iteml меньше, чем item2. Если элементы равны, функция должна возвращать ноль-
В нашем случае сравнивались координаты Х двух точек. В результате такой сортировки по возрастанию объекты оказались расположены так, что первый элемент списка указывает на объект с минимальной координатой X, а последний — на объект с максимальной координатой X.
После сортировки осталось заново пронумеровать все точки. Это делает цикл в методе-обработчике SortBtnClick. Обратите внимание на примененный в этом случае способ приведения типа, обеспечивающий обращение к свойствам экземпляров класса TMypixel.
Метод-обработчик DeiBtnciick обеспечивает полную очистку списка pixList.
Коллекции
Коллекция представляет собой разновидность списка указателей, оптимизированную для работы с объектами определенного вида. Сама коллекция инкапсулирована в классе тсо1lection. Элемент коллекции должен быть экземпляром класса, унаследованного от класса TCollectionitem. Это облегчает программирование и позволяет обращаться к свойствам и методам объектов напрямую.
Рис. 7.1. Редактор коллекции
Коллекции объектов широко используются в компонентах VCL. Например, панели компонента TCoolBar (см. гл. 11) объединены в коллекцию. Класс TCoolBands, объединяющий панели, является потомком класса TCoilection. А отдельная панель является экземпляром класса TCoolBar, происходящего от класса TCollectionitem.
Поэтому знание свойств и методов классов коллекции позволит успешно использовать их при работе со многими компонентами (TDBGrid, TListView, TStatusBar, TCoolBar И Т. Д.).
Для работы с коллекцией, независимо от инкапсулирующего ее компонента, применяется специализированный Редактор коллекции (рис. 7.1), набор элементов управления которого может немного изменяться для разных компонентов.
Список Редактора объединяет элементы коллекции. При выборе одной строки из списка свойства объекта коллекции становятся доступны в Инспекторе объектов. В список можно добавлять новые элементы и удалять существующие, а также менять их взаимное положение.
Примеры использования коллекций представлены при описании соответствующих компонентов.
Класс TCollection
Класс TCollection является оболочкой коллекции, обеспечивая разработчика набором свойств и методов для управления ею.
Сама коллекция содержится в свойстве
property Items[Index: Integer]: TCollectionItem;
Полное объявление свойства в классе выглядит следующим образом:
property Items[Index: Integer]: TCollectionItem read Getltem write Setltem;
Методы Getitem и setitem обращаются к внутреннему полю Fitems:
FItems: TList;
Именно оно хранит коллекцию объектов во время выполнения. Отсюда следует, что коллекция представляет собой список указателей на экземпляры класса TCollectionItem или его потомка. А класс TCollection обеспечивает удобство использования элементов списка.
Таблица 7.4. Свойства и методы класса TCollection
Объявление |
Описание |
Property Count: Integer; |
Возвращает число элементов коллекции |
Type TcollectionItemClass = class of Tcollectionltem; |
Возвращает класс-потомок TCollectionItem, экземпляры которого собраны в коллекции |
Property ItemClass: TcollectionItemClass; |
|
Property Items[Index: Integer]: Tcollectionltem; |
Коллекция экземпляров класса |
Function Add: Tcollectionltem; |
Добавляет к коллекции новый экземпляр класса |
Procedure Assign(Source: TPersistent); override; |
Копирует коллекцию из объекта Source в данный объект |
Procedure BeginUpdate; virtual; |
Отменяет перерисовку коллекции. Используется при внесении изменений в коллекцию |
Procedure Clear; |
Удаляет из коллекции все элементы |
Procedure EndUpdate; virtual; |
Отменяет действие метода BeginUpdate |
Function FindItemID(ID: Integer): Tcollectionltem; |
Возвращает объект коллекции с номером id |
Function GetNamePath: string; override; |
Возвращает имя класса коллекции во время выполнения, если коллекция не имеет владельца. Иначе возвращает название свойства класса, владеющего коллекцией |
Function Insert(Index: Integer): Tcollectionltem; |
Вставляет в коллекцию новый объект на место с номером Index |
Класс Tcollectionltem
Класс Tcollectionltem инкапсулирует основные свойства и методы элемента коллекции. Свойства класса обеспечивают хранение информации о расположении элемента в коллекции.
Таблица 7.5. Свойства и методы класса Tcollectionltem
Объявление |
Описание |
Property Collection: Tcollection; |
Содержит экземпляр класса коллекции, которой принадлежит данный элемент |
Property DisplayName: string; |
Содержит имя элемента, которое представляет его в Редакторе коллекции |
property ID: Integer; |
Содержит уникальный номер элемента в коллекции, который не может изменяться |
property Index: Integer; |
Содержит порядковый номер элемента в коллекции. Он соответствует положению элемента в списке и может изменяться |
Резюме
Списки, объединяющие элементы различных типов, играют важную роль при создании программной логики приложения. В Delphi используются три основных вида списков.
В среде Delphi вы можете найти еще много полезных классов общего применения. В модуле classes. раз есть класс TBits, обеспечивающий побитное чтение и запись информации. В модуле contnrs.pas есть классы TStack и TQueue (стек и очередь), а также потомки TList — TClassList, TComponentList и т. д. Они помогут вам решать типовые задачи быстро, избегая "изобретения велосипеда".