.
TObject, демонстрация использования self (ClassType,TClass,self,TList,Pointer,Add, TClass)
Автор megabax   
15.10.2009 г.
New Page 2

TObject, демонстрация использования self (ClassType,TClass,self,TList,Pointer,Add, TClass)

Исходники к уроку можно скачать здесь.

Сегодня мы изучим функции ClassType. Реализован он очень просто:

function TObject.ClassType: TClass;
begin
      Pointer(Result) := PPointer(Self)^;
end;

Тип PPointer является указателем на указатель и объявлен следующим образом:

PPointer = ^Pointer;

 А TClass объявлен:

TClass = class of TObject;

Переменная Self является ссылкой на сам объект. Что бы было лучше понятно, что такое self и для чего он нужен, давайте рассмотрим пример. Предположим, у нас есть некий контейнер объектов, связанный с некоторым компонентом визуального отображения, в котором отображаются объекта нашего контейнера. При изменении любого из объектов в контейнере необходимо перерисовать визуальный объект. Но вот проблема - сам объект "не знает", что он принадлежит контейнеры. Как решить эту проблему? Логично предположить, в объекте нужно предусмотреть некоторое поле, указывающее на контейнер, которое будет заполнятся при добавлении объекта в контейнер. Можно каждый раз, когда добавляем, еще и "не забыть" присвоить указатель на контейнер. Но согласитесь, это не очень удобно. А вот как же сделать так, что бы эти действия производить в одном методе? Как раз с помощью указателя self. Давайте попробуем написать класс контейнер, содержащий слова. При измении любого слова в контейнере список слов должен обновиться. Для начала объявим  объявим класс слова и класс контейнера:

TWordSelfDemo=class(TObject)
private
      FOwner:pointer;
protected
      FWord:string;
      procedure SetWord(AWord:string);
public
      property Word:string read FWord write SetWord;
end;

TWordsContainerSelfDemo=class(TList)
private
     procedure Refresh;
protected
     FListBox:TListBox;
     procedure SetListBox(AListBox:TListBox);
public
     function Add(Item: Pointer): Integer;
     procedure delete(index:integer);
     property ListBox:TListBox read FListBox write SetListBox;
end;
 

Теперь немножко комментариев к коду. Класс TWordSelfDemo у нас будет хранить слово (его текст). Для задания слова используем свойство word, которое имеет метод для записи SetWord. При реализации этого метода мы предусмотрим обновление визуального компонента, связанного с контейнером.

Класс TWordsContainerSelfDemo - собственно говоря, сам контейнер. Это класс, дочерний от TList. В нем переопределены методы Add и Delete, а так же добавлен метод Refresh для обновления связанного с контейнером визуального компонента. Для связи с визуальным компонентом используется свойство ListBox, устанавливаемое методом SetListBox.

А теперь перейдем к реализации. Опишем метод SetWord

procedure TWordSelfDemo.SetWord(AWord:string);
begin
     if FWord=AWord then exit;
     FWord:=AWord;
     if FOwner<>nil then TWordsContainerSelfDemo(FOwner).Refresh;
end;

В данной процедуре происходит проверка, что мы меняем содержимое свойства Word - если пытаемся записать повторно то же самое слово - ничего не делаем.  Затем присваиваем новое значение внутреннему полю и запускаем метод Refresh класса контейнера. При чем стоит обратить внимание на то, что нужно еще и убедиться, что наше объект действительно находиться в контейнере - значение поля  FOwner проверяем на nil.

Все, у класса слова у нас только один метод. Мы его реализовали. Теперь займемся контейнером. Реализуем его метод Add:

function TWordsContainerSelfDemo.Add(Item: Pointer): Integer;
begin
    inherited Add(Item);
    TWordSelfDemo(Item).FOwner:=self;
    refresh;
end;

В данной функции мы вызываем метод Add родительского класса (TList), затем устанавливаем указатель на себя при помощи ключевого слова self:

    TWordSelfDemo(Item).FOwner:=self;

И, наконец, обновляем визуальный компонент - вызываем refresh.

Сам метод refresh будет у нас выглядеть так:

procedure TWordsContainerSelfDemo.Refresh;
var i,cn:integer;
begin
     FListBox.Clear;
     cn:=Count-1;
     for i:=0 to cn do FListBox.Items.Add(TWordSelfDemo(Items[i]).FWord);
end;

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

И, наконец, последние два метода, они комментариев, думаю, не требуют:

procedure TWordsContainerSelfDemo.SetListBox(AListBox:tListBox);
begin
     FListBox:=AListBox;
     Refresh;
end;

procedure TWordsContainerSelfDemo.delete(index:integer);
begin
    TWordSelfDemo(Items[index]).Free;
    inherited Delete(index);
    refresh;
end;

А теперь сделаем форму*:

TObject, демонстрация использования self (ClassType,TClass,self,TList,Pointer,Add, TClass)

Саму форму назовем SelfDemoForm, кнопочки btnAdd (добавить), btnDelete (удалить) и btnEdit (редактировать).  Сроку для ввода слова назовем edWord, а сипоск слов (компонент TListBox) обзовем lbWords.

Теперь напишем для наших кнопок обработчики событий:

procedure TSelfDemoForm.btnAddClick(Sender: TObject);
var wd:TWordSelfDemo;
begin
    wd:=TWordSelfDemo.Create;
    wd.Word:=edWord.Text;
    FWordsContainerSelfDemo.Add(wd);
end;

procedure TSelfDemoForm.btnDeleteClick(Sender: TObject);
begin
    if lbWords.ItemIndex>=0 then FWordsContainerSelfDemo.Delete(lbWords.ItemIndex);
end;

procedure TSelfDemoForm.btnEditClick(Sender: TObject);
begin
   if lbWords.ItemIndex>=0 then TWordSelfDemo(FWordsContainerSelfDemo[lbWords.ItemIndex]).Word:=edWord.Text;
end;

но это еще не все, нам нужно объявить в классе формы поле контейнера и написать обработчики события создания и удаления формы:

TSelfDemoForm = class(TForm)
   lbWords: TListBox;
   btnAdd: TButton;
   edWord: TEdit;
   btnDelete: TButton;
   btnEdit: TButton;
   procedure FormCreate(Sender: TObject);
   procedure FormDestroy(Sender: TObject);
   procedure btnAddClick(Sender: TObject);
   procedure btnDeleteClick(Sender: TObject);
   procedure btnEditClick(Sender: TObject);
private
  FWordsContainerSelfDemo:TWordsContainerSelfDemo;
public
{ Public declarations }
end;
 
procedure TSelfDemoForm.btnDeleteClick(Sender: TObject);
begin
    if lbWords.ItemIndex>=0 then FWordsContainerSelfDemo.Delete(lbWords.ItemIndex);
end;

procedure TSelfDemoForm.btnEditClick(Sender: TObject);
begin
   if lbWords.ItemIndex>=0 then TWordSelfDemo(FWordsContainerSelfDemo[lbWords.ItemIndex]).Word:=edWord.Text;
end;

Все, теперь можно запустить пример (исходники можно скачать здесь):

TObject, демонстрация использования self (ClassType,TClass,self,TList,Pointer,Add, TClass)

 

И так, мы с вами поняли, что же такое указатель self. Теперь вернемся к нашему пресловутому TObject. Рассмотрим еще раз строку:

 Pointer(Result) := PPointer(Self)^;

Мы присваиваем результату функции значение, находящееся там, куда указывает self, при чем сам указатель мы преобразуем к PPointer, поскольку прямое присваивание вызовет у нас ошибку на этапе компиляции: Pointer type required (ожидается тип Pointer).

Что же у нас содержаться по адресу, на который указывает self? А там у нас находиться, собственно говоря, сам класс (TClass).

- Для чего его можно использовать на практике? - спросите вы.

- Допустим, - отвечу я, - вы хотите создать экземпляр класса неизвестного заранее типа. Вы передаёте в неё переменную классового типа и указатель на результат.

Практически в любой программе используется метод:

procedure TApplication.CreateForm(InstanceClass: TComponentClass; var Reference);

 где TComponentClass объявлен как

TComponentClass = class of TComponent;


Метод CreateForm применяется в главной части программы, для создания форм:

program ProjectTClass;

uses
Forms,
UnitTClass in 'UnitTClass.pas' {frmTClassDemo};

{$R *.res}

begin
     Application.Initialize;
     Application.CreateForm(TfrmTClassDemo, frmTClassDemo);
     Application.Run;
end.

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

procedure TMyForm.tvMyTreeViewCreateNodeClass(Sender: TCustomTreeView;var NodeClass: TTreeNodeClass);
begin
     //некий код
end;

 Но это мы уже рассмотрим в следующем уроке.


Скриншоты, помеченные знаком * , являются цитатами и иллюстрациями  программного продукта "Delphi", авторское право на который принадлежит "Borland Software Corporation".


 

Последнее обновление ( 24.05.2013 г. )